Unix域编程流程简单梳理

本文深入介绍了Unix域编程,这是一种在同一主机上实现高效进程间通信的方法。Unix域socket允许进程间直接交换数据,其效率高于TCPsocket,并能传递描述符和安全凭证。文章详细阐述了Unix域编程的流程、地址格式、注意事项,并提供了客户端和服务端的示例代码,展示了如何创建和使用Unix域socket进行通信。
摘要由CSDN通过智能技术生成

Unix域编程作用

Unix域编程用于同一台主机内部的进程之间的客户端/服务端通信,使用和网络socket编程相同的API。Unix域编程既可以使用SOCK_STREAM类型的socket,也可以使用SOCK_DGRAM类型的socket,二者使用的接口稍微不同。Unix域编程的优势包括:

  1. 效率高,比使用TCPsocket编程效率高一倍。
  2. 可以实现在同一主机的不同进程之间传递描述符。
  3. 可以实现把客户的凭证提供给服务器,提供额外的安全措施。

Unix域编程流程

针对SOCK_STREAM类型socket,编程流程如下所示,流程和TCP编程一致。

服务端:socket—>bind—>listen—>accept—>send/recv—>closesocket

客户端:socket—>bind(可选)—>connect—>send/recv---->closesocket

针对SOCK_DGRAM类型的socket,编程流程如下所示,和UDP流程不同的地方在于,UDP客户端可以不调用bind,内核会自动选择客户端地址,同时服务端通过recvfrom客户获取到这个地址,实现后续的双方通信。但是Unix域编程中,内核不会自动分配客户端地址,于是服务端就无法获取客户端地址,影响后续的通信,但是SOCK_STREAM类型的Unix域编程没有这个问题,虽然调用bind和connect不会自动分配地址,但是服务端通过accept返回的文件描述符,可以用来和客户端的通信:

服务端:socket()创建套接字 --> bind()绑定监听地址 --> recvfrom()等待接收客户端数据 --> sendto()发送应答数据给客户端

客户端:socket()创建套接字 -->bind()绑定客户端地址–> sendto()向服务端发送数据 --> recvfrom()接收服务端应答数据 --> close()关闭套接字

Unix域编程的地址格式

Unix域编程和网络域编程使用同一套接口,也需要使用地址结构,它的地址结构如下所示:

#include <sys/un.h>
struct sockaddr_un {
    sa_family_t sun_family;//AF_LOCAL
    char sun_path[104];//绝对路径,并且文件不存在。可以使用空字符串,含义类似INADDR_ANY。
}

Unix编程注意事项

  1. sockaddr_un.sun_path中的文件必须不存在,否则调用bind的时候会报错,因此再调用bind之前最好先使用unlink删除这个路径名。bind成功后,使用ls -l可以看到文件的类型是s(socket)类型。
[root@localhost ~]# ll /tmp/unix.str
srwxr-xr-x 1 root root 0 Mar 19 18:40 /tmp/unix.str
  1. 创建socket的时候地址族选择AF_LCOAL,类型选择SOCK_STREAM或SOCK_DGRAM,协议用0.
  2. SOCK_DGRAM类型的socket,客户端必须调用bind,SOCK_STREAM则没有这个要求。

Unix编程简单示例

附上unp书上的两个实例:

客户端实例

#include "unp.h"

void str_cli(FILE *fp, int sockfd);

int 
main(int argc, char **argv)
{
	int sockfd, n;
	struct sockaddr_un servaddr, cliaddr;
	char buff[128];

	sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
	bzero(&cliaddr, 0);
	cliaddr.sun_family = AF_LOCAL;
	strncpy(cliaddr.sun_path, "/tmp/cli_str", sizeof(cliaddr));
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strncpy(servaddr.sun_path, UNIXSTR_PATH, sizeof(servaddr.sun_path) - 1);

	bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
	
	connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));

	if ((n = recv(sockfd, buff, sizeof(buff), 0)) > 0) {
		printf("recv : %s", buff);
	}

	exit(0);
}

服务端实例

#include "unp.h"

int 
main(int argc, char **argv)
{
	int listenfd, connfd;
	pid_t childpid;
	socklen_t clilen;
	struct sockaddr_un cliaddr, servaddr;
	char buff[128];

	listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);

	unlink(UNIXSTR_PATH);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strncpy(servaddr.sun_path, UNIXSTR_PATH, sizeof(servaddr.sun_path) - 1);
	printf("server bind to %s\n", UNIXSTR_PATH);
	bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	
	listen(listenfd, LISTENQ);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen) ) < 0) {
			perror("accept failed");
		}
		printf("recv client path %s\n", cliaddr.sun_path);
		if ((childpid = fork()) == 0) {
			close(listenfd);
			strncpy(buff, "hello", sizeof(buff));
			send(connfd, buff, sizeof(buff), 0);
			exit(0);
		}
		close(connfd);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值