Linux进程间通信-----使用数据报套接字实现两个进程之间的通信

时间:2021-08-29 03:56:35

socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。与流套接字不同,用类型SOCK_DGRAM指定的,它不需要建立连接和维持一个连接。本文示例代码为本地两个进程之间的通信,创建socket域为AF_UNIX。

服务端

首先服务器应用程序用系统调用socket来创建一个套接字,它是系统分配给该服务器进程的类似文件描述符的资源,它不能与其他的进程共享。接下来,服务器进程会给套接字起个名字(监听),使用系统调用bind来给套接字命名。然后服务器进程就开始等待客户连接到这个套接字。然 后系统调用recvfrom来接收来自客户程序发送过来的数据。如果需要,服务器程序对数据进行相应的处理,再通过系统调用sendto把处理后的数据发送回客户程序。需要特别之处的chmod函数,如果没有改变文件的属性,服务端可以获得客户端发过来的信息,但是发送回客户端将出现errno=EADV的错误。即要实现双向通信,chmod函数的调用时很有必要的。
示例代码:
#include <sys/socket.h>  
#include <sys/un.h>  
#include <sys/types.h>
#include <errno.h>

int main(int argc ,char *argv[])  
{
  int sockfd = 0;  
  struct sockaddr_un addr;
  struct sockaddr_un from;
  socklen_t fromlen = sizeof(from);
  char *reply = NULL;
  const int reply_size = 40;
  char recv_buf[256] = "";  
  int reply_len = 0;
  char fname[10]="tmp/test";


    unlink(fname);
    addr.sun_family = AF_UNIX;  
    strcpy(addr.sun_path,fname);  
  
    sockfd = socket(AF_UNIX,SOCK_DGRAM,0);  
    if(sockfd < 0 )  
    {  
        printf("socket error");  
    }  
  
    if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) < 0)  
    {  
        printf("bind error");  
        close(sockfd);  
        return 0;
    }  
    printf("Bind is ok\n");  
	
    if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
	printf("chmod[ctrl_interface/ifname]");
    }


     res = recvfrom(sockfd,recv_buf,sizeof(recv_buf),0,(struct sockaddr*)&from, &fromlen);
     recv_buf[res] = '\0';
     printf("Recv: %s\n",recv_buf); 
		
	reply = malloc(reply_size);
	if (reply == NULL) {
		sendto(sockfd, "FAIL\n", 5, 0, (struct sockaddr *) &from,fromlen);
		 printf("malloc error.\n");  
		return 0;
	}
	memset(reply,0,reply_size);
	memcpy(reply, "OK.", 3);
	reply_len = 3;	
	
	res = sendto(sockfd, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
	printf("res=%d.errno = %d\n",res,errno);
	free(reply);
	close(sockfd);
}
客户端

首先客户端应用程序也是用系统调用socket来创建一个套接字,然后调用connect()连接就服务器。收发数据可用recvfrom()和sendto(),也可用recv()和send()。由于recvfrom和recv调用时阻塞的,但是我们又不想阻塞当前进程,即要用非阻塞方式调用,那么可用fcntl()来设定进程为非阻塞形式,select来查询服务端返回的信息。使用Select可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

#define PATH "tmp/test"
int main(int argc ,char *argv[])  
{
	int connect_fd = 0;  
	struct sockaddr_un addr; 
	int ret,res;
	int flags;
	char snd_buf[24],recv_buf[32];  
	fd_set rfds;
	struct timeval tv;

	memset(&addr, 0, sizeof(addr));  
	addr.sun_family = AF_UNIX;  
	strcpy(addr.sun_path,PATH);  

	connect_fd = socket(AF_UNIX,SOCK_DGRAM,0);  
	if(connect_fd < 0)  
	{  
		printf("socket error\n");  
		return 0;
	} 

	//connect server  
	ret=connect(connect_fd,(struct sockaddr*)&addr,sizeof(addr));  
	if(ret==-1)  
	{  
		printf("cannot connect to the server\n");  
		close(connect_fd);  
		return 0;  
	}
	flags = fcntl(connect_fd, F_GETFL);
	if (flags >= 0) {
		flags |= O_NONBLOCK;
		if (fcntl(connect_fd, F_SETFL, flags) < 0) {
			printf("fcntl(ctrl->s, O_NONBLOCK)");
		}
	}
	
	memset(snd_buf,0,24);  
	strcpy(snd_buf,"message from client");  
	//send info server  
	send(connect_fd,snd_buf,sizeof(snd_buf),0); 

	//read
	memset(recv_buf,0,32); 
	for(;;){
		tv.tv_sec = 10;
		tv.tv_usec = 0;
		FD_ZERO(&rfds);
		FD_SET(connect_fd, &rfds);
		res = select(connect_fd + 1, &rfds, NULL, NULL, &tv);
		if (res < 0)
			return res;
			if (FD_ISSET(ctrl->s, &rfds)) {
			res = recv(connect_fd, recv_buf, sizeof(recv_buf), 0);
			if (res < 0)
				return res;
			if (res > 0 && reply[0] == '<') {				
				continue;
			}
			printf("ret=%d.errno = %d,receive info:%s\n",ret,errno,recv_buf);
			break;
		} else {
			return -2;
		}
	}
	close(connect_fd);   
}