linux 进程间使用unix socket通信


前言:前些天实现了unix socket的通信,本想完完全全自己写一篇博客记录下来,但写的时候发现对于socket知识的理解还有所欠缺,故引用其他博客写的比较好的部分综合一下,这样让大家更容易理解。

一、Unix socket概述(参考于博客http://blog.csdn.net/bingqingsuimeng/article/details/8470029):

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIXDomain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。


二、socket工作流程:

服务端:

1. socket:      建立一个socket

2. bind:          将这个socket绑定在某个文件上(AF_UNIX)或某个端口上(AF_INET),我们会分别介绍这两种。

3. listen:        开始监听

4. accept:      如果监听到客户端连接,则调用accept接收这个连接并同时新建一个socket来和客户进行通信

5. read/write:读取或发送数据到客户端

6. close:        通信完成后关闭socket


客户端:

1. socket:      建立一个socket

2. connect:   主动连接服务器端的某个文件(AF_UNIX)或某个端口(AF_INET)

3. read/write:如果服务器同意连接(accept),则读取或发送数据到服务器端

4. close:        通信完成后关闭socket


如图所示(此图参考于博客http://blog.csdn.net/hguisu/article/details/7445768/,虽然是网络TCP的传输流程图,但原理流程是一样的):




三、unix socket的具体实现:

本例实现的功能:在linux下建立三个进程(分别是客户端socket发送进程、客户端socket接受进程、服务端),使用unix socket实现进程间的通信。

本文代码实现参考于http://blog.csdn.net/shanzhizi/article/details/16882087

客户端的作用是发送数据至服务端或从服务端接受数据。

服务端的作用是监听客户端的请求并处理。


在本例中,我对原代码做了一点改动,原代码中是client发送四次数据至server,server接收四次数据并显示。我改成了server一直接收连接并根据我定制的小协议来进行下一次的数据传输方向。

本协议每次完整的数据传输分为两个步骤:

1、在server端绑定socket并开启监听后,client会先发送一次命令数据,由命令数据决定下一次的数据方向。

2、client和server会依据命令数据,决定下一次是接受数据还是发送数据,以便做不同的处理。


#define READ_FROM_CLIENT 0X01
#define WRITE_TO_CLIENT 0x02

代码实现如下:

服务端server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define UNIX_DOMAIN "/tmp/UNIX.domain"
#define READ_FROM_CLIENT 0X01
#define WRITE_TO_CLIENT 0x02

int main(void)
{
	socklen_t clt_addr_len;
	int listen_fd;
	int com_fd;
	int ret;
	//int i;
	static char data_buf[1024];
	int len;
	struct sockaddr_un clt_addr;
	struct sockaddr_un srv_addr;
	listen_fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if(listen_fd < 0) {
		perror("cannot create communication socket");
		return 1;
	}
	//set server addr_param
	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path,UNIX_DOMAIN);
	unlink(UNIX_DOMAIN);
	//bind sockfd & addr
	ret = bind(listen_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if(ret == -1) {
		perror("cannot bind server socket");
		close(listen_fd);
		unlink(UNIX_DOMAIN);
		return 1;
	}
	//listen sockfd
	ret = listen(listen_fd,1);
	if(ret == -1) {
		perror("cannot listen the client connect request");
		close(listen_fd);
		unlink(UNIX_DOMAIN);
		return 1;
	}
	while(1) {
		//have connect request use accept
		len = sizeof(clt_addr);
		com_fd = accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
		if(com_fd < 0) {
			perror("cannot accept client connect request");
			close(listen_fd);
			unlink(UNIX_DOMAIN);
			return 1;
		}
		//read and printf sent client info
		printf("\nReceive from client:\n");	
		memset(data_buf, 0, 1024);
		read(com_fd, data_buf, sizeof(data_buf));
		printf("%d Request is %X\n", data_buf[0]);
		//Read from client
		if(data_buf[0] == READ_FROM_CLIENT) {
			memset(data_buf, 0, 1024);
			read(com_fd, data_buf, sizeof(data_buf));
			printf("The data read from client is %s\n:",data_buf);
		}
		//Send to client
		if(data_buf[0] == WRITE_TO_CLIENT) {
			memset(data_buf, 0, 1024);
			strcpy(data_buf, "message from server!!");
			write(com_fd, data_buf, sizeof(data_buf));
			printf("The data send to client is %s\n:",data_buf);
		}
		memset(data_buf, 0, 1024);
		close(com_fd);
	}
	
	//close(listen_fd);
	//unlink(UNIX_DOMAIN);
	//return 0;
}

客户端client_sent.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define UNIX_DOMAIN "/tmp/UNIX.domain"

int main(void)
{
	int connect_fd;
	int ret;
	char snd_buf[1024];
	static struct sockaddr_un srv_addr;
	//create unix socket
	connect_fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if(connect_fd < 0) {
		perror("cannot create communication socket");
		return 1;
	}
	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path,UNIX_DOMAIN);
	//connect server
	ret = connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if(ret == -1) {
		perror("cannot connect to the server");	
		close(connect_fd);
		return 1;
	}
	memset(snd_buf, 0, 1024);
	snd_buf[0] = 0x01;
	//send command
	write(connect_fd, snd_buf, sizeof(snd_buf));
	//send info server
	memset(snd_buf, 0, 1024);
	strcpy(snd_buf, "message from client");
	write(connect_fd, snd_buf, sizeof(snd_buf));
	close(connect_fd);
	return 0;
}

客户端client_receive.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#define UNIX_DOMAIN "/tmp/UNIX.domain"

int main(void)
{
	int connect_fd;
	int ret;
	char snd_buf[1024];
	static struct sockaddr_un srv_addr;
	//create unix socket
	connect_fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if(connect_fd < 0) {
		perror("cannot create communication socket");
		return 1;
	}
	srv_addr.sun_family = AF_UNIX;
	strcpy(srv_addr.sun_path,UNIX_DOMAIN);
	//connect server
	ret = connect(connect_fd, (struct sockaddr*)&srv_addr, sizeof(srv_addr));
	if(ret == -1) {
		perror("cannot connect to the server");	
		close(connect_fd);
		return 1;
	}
	memset(snd_buf, 0, 1024);
	snd_buf[0] = 0x02;
	//send command
	write(connect_fd, snd_buf, sizeof(snd_buf));
	//read from server
	memset(snd_buf, 0, 1024);
	read(connect_fd, snd_buf, sizeof(snd_buf));
	printf("The data read from server is %s\n:",snd_buf);
	close(connect_fd);
	return 0;
}

使用

#gcc server.c -o server

#gcc client_sent -o client_sent

#gcc client_receive -o client_receive

分别生成对应的可执行文件

先在后台运行服务端:

#./server &

再直接运行客户端的发送程序和接受程序便可得到结果。(#./client_sent    #./client_receive)


ps:本例中采用的read和write函数都是阻塞函数,会导致进程一直处于阻塞状态,若想了解非阻塞的传输方式,请参考博客http://blog.csdn.net/guxch/article/details/7041052。

若有什么不明白或技术交流,请留言,谢谢。

  • 6
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux中,进程通信可以使用socket来实现。Socket是一种特殊的文件,它是应用层与TCP/IP协议族通信的中软件抽象层,提供了一组简单的接口来组织数据,以符合指定的协议。在网络编程中,大部分的通信都是通过socket实现的。 使用TCP/IP协议的应用程序通常采用socket接口来实现网络进程通信。无论是UNIX BSD的套接字还是UNIX System V的TLI(已经被淘汰),几乎所有的应用程序都是采用socket来进行通信。 此外,还有一种叫做Unix domain sockets的通信方式,它使用系统文件的地址作为进程通信的身份,并且仅在系统内核内部进行通信,不会在网络中传播。两个进程可以同时打开一个Unix domain socket来进行通信。 总结来说,Linux中的进程通信可以通过socket来实现,使用TCP/IP协议的应用程序通常采用socket接口进行通信,并且还可以使用Unix domain sockets进行通信。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [LINUX进程网络通信--SOCKET](https://blog.csdn.net/qq_44370382/article/details/107959541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [About AF_LOCAL in Linux](https://blog.csdn.net/frank_jb/article/details/77199834)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值