linux 进程间使用unix socket通信

Linux 专栏收录该内容
5 篇文章 0 订阅


前言:前些天实现了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。

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

  • 3
    点赞
  • 1
    评论
  • 24
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值