利用套接字实现本地通信

使用套接字除了可以实现网络间不同主机间的通信外,还可以实现同一主机的不同进程间的通信,且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口,只是地址结构与某些参数不同。

其主要流程如下:



代码实现如下:

客户端:

[cpp]  view plain  copy
  1. //client  
  2. #include<stdio.h>  
  3. #include<string.h>  
  4. #include<sys/types.h>  
  5. #include<sys/socket.h>  
  6. #include<sys/un.h>  
  7. #define UNIX_DOMAIN "/home/zhangmiaoling/test/socket/UNIX.domain"  
  8. int main(){  
  9.     int connect_fd;  
  10.     int ret;  
  11.     char send_buff[1024];  
  12.     int i;  
  13.     static struct sockaddr_un srv_addr;  
  14.     // creat unix socket  
  15.     connect_fd=socket(PF_UNIX,SOCK_STREAM,0);  
  16.     if(connect_fd<0){  
  17.     perror("cannot creat socket");  
  18.     return -1;  
  19.     }  
  20.     srv_addr.sun_family=AF_UNIX;  
  21.     strcpy(srv_addr.sun_path,UNIX_DOMAIN);  
  22.     //connect server  
  23.     ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  24.     if (ret<0){  
  25.     perror("cannot connect server");  
  26.     close(connect_fd);  
  27.     return -1;  
  28.     }  
  29.     memset(send_buff,0,1024);  
  30.     strcpy(send_buff,"message from client");  
  31.     //send info server  
  32.     write(connect_fd,send_buff,sizeof(send_buff));  
  33.     close(connect_fd);  
  34.     return 0;  
  35. }  

服务器端
[cpp]  view plain  copy
  1. //server  
  2. #include<stdio.h>  
  3. #include<sys/socket.h>  
  4. #include<sys/types.h>  
  5. #include<sys/un.h>  
  6. #define UNIX_DOMAIN "/home/zhangmiaoling/test/socket/UNIX.domain"  
  7.   
  8. int main(){  
  9.   
  10.     socklen_t clt_addr_len;  
  11.     int listen_fd;  
  12.     int com_fd;  
  13.     int ret;  
  14.     int i;  
  15.     static char rcv_buff[1024];  
  16.     int len;  
  17.     struct sockaddr_un clt_addr;  
  18.     struct sockaddr_un srv_addr;  
  19.     listen_fd=socket(AF_UNIX,SOCK_STREAM,0);  
  20.     if(listen_fd<0){  
  21.     perror("connect creat communication socket");  
  22.     }  
  23.     // set srv_addr param  
  24.     srv_addr.sun_family=AF_UNIX;  
  25.     strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);  
  26.     unlink(UNIX_DOMAIN);  
  27.     //bind sockfd&addr  
  28.     ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));  
  29.     if(ret<0){  
  30.     perror("cannot bind server socket");  
  31.     close(listen_fd);  
  32.     unlink(UNIX_DOMAIN);  
  33.     return -1;  
  34.     }  
  35.     //listen sockfd  
  36.     ret=listen(listen_fd,1);  
  37.     if(ret<0){  
  38.      perror("cannot listen sockfd");  
  39.      close(listen_fd);  
  40.      unlink(UNIX_DOMAIN);  
  41.      return -1;  
  42.     }  
  43.     //have connect requst use accept  
  44.     len=sizeof(clt_addr);  
  45.     com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);  
  46.     if(com_fd<0){  
  47.     perror("cannot accept requst");  
  48.     close(listen_fd);  
  49.     unlink(UNIX_DOMAIN);  
  50.     return -1;  
  51.     }  
  52.     //read and printf client send info  
  53.     printf("\n******info********\n");  
  54.     //for(i=0,i<4,i++){  
  55.     for(i=0;i<4;i++){  
  56.     memset(rcv_buff,0,1024);  
  57.     int num = read(com_fd,rcv_buff,sizeof(rcv_buff));  
  58.     printf("message from client %d : %s\n",num,rcv_buff);  
  59.     }  
  60.     close(com_fd);  
  61.     close(listen_fd);  
  62.     unlink(UNIX_DOMAIN);  
  63.     return 0;  
  64.   
  65. }  

一. 创建socket

   创建socket,类型为AF_LOCALAF_UNIX,表示用于进程通信:

调用函数socket(),其原型如下:

int socket(int domain, int type, int protocol);

参数:

domain:指定协议族,对于本地套接字来说,值必须设置为AF_UNIX枚举值;

type:指定套接字类型,可以被设置为SOCK_STREAM(流式套接字)活SOCK_DGRAM(数据报式套接字)

protocol:指定具体的协议,应被设置为0

扫描二维码关注公众号,回复: 126062 查看本文章

返回值为生成的套接字描述符。

对于本地套接字来说,流式套接字(SOCK_STREAM)是一个有顺序的、可靠的双向字节流,相当于在本地进程之间建立起一条数据通道;数据报式套接字(SOCK_DGRAM)相当于单纯的发送消息,在进程通信过程中,理论上可能会有信息丢失、复制或者不按先后次序到达的情况,但由于其在本地通信,不通过外界网络,这些情况出现的概率很小。

二. 设置socket参数

   SOCK_STREAM式本地套接字的通信双方均需要有本地地址,其中服务器端的本地地址需要明确指定,指定方法是使用struct sockaddr_un类型的变量

struct sockaddr_un{

  sa_family_t    sun_family;        // AF_UNIX

  char    sun_path[UNIX_PATH_MAX];  // 路径名

}

三. 绑定

    绑定要使用 bind 系统调用,其原形如下:    

int bind(int socket, const struct sockaddr *address, size_t address_len);

参数

socket:服务端套接字描述符

address:需要绑定的服务端本地地址

address_len:本地地址的字节长度


四. 监听

    服务器端套接字创建完毕并赋予本地地址值(名称,本例中为CAN_SERVICE)后,需要进行监听,等待客户端连接并处理请求,监听使用 listen 系统调用,接受客户端连接使用accept系统调用,它们的原形如下:
int listen(int socket, int backlog);
int accept(int socket, struct sockaddr *address, size_t *address_len);
参数

socket:表示服务器端的套接字描述符;

backlog 表示排队连接队列的长度(若有多个客户端同时连接,则需要进行排队);

address 表示当前连接客户端的本地地址,该参数为输出参数,是客户端传递过来的关于自身的信息;

address_len 表示当前连接客户端本地地址的字节长度,这个参数既是输入参数,又是输出参数。实现监听、接受和处理。

五. 连接

    客户端需要socket系统调用connect()连接到服务端,其函数原型如下:

int connect(int socket, const struct sockaddr *address, size_t address_len);
参数

socket:客户端的套接字描述符

address:当前客户端的本地地址,是一个 struct sockaddr_un 类型的变量

address_len:表示本地地址的字节长度

五. 数据交互

    无论客户端还是服务器,都要和对方进行数据上的交互。一个进程扮演客户端的角色,另外一个进程扮演服务器的角色,两个进程之间相互发送接收数据,这就是基于本地套接字的进程通信。

    循环读取客户端发送的消息,当客户端没有发送数据时会阻塞直到有数据到来。如果想要多个连接并发处理,需要创建线程,将每个连接交给相应的线程并发处理。接收到数据后,进行相应的处理,将结果返回给客户端。发送和接收数据要使用 write 和 read 系统调用,它们的原形为:
int read(int socket, char *buffer, size_t len);
int write(int socket, char *buffer, size_t len);

猜你喜欢

转载自blog.csdn.net/handsomehong/article/details/79787849