域套接字-多客户端实现
我们在linux 应用开发中,如果需要实现进程间通信的多客户端接口,就需要引入select 和epoll 相关机制了,本文介绍下epoll的功能的实现。
多客户端使用场景,比如我们需要开发一个库接口,提供给客户调用,那么使用域套接字实现进程间通信,就必须在service端引入多路复用,否则库功能就只能被一个进程使用了。
代码实现:
service端
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/un.h>
#include<sys/epoll.h>
#define dbg_printf(...) printf("[%s.%d]",__func__,__LINE__);printf(__VA_ARGS__)
#define UNIX_SOCKET_FILE "/home/linux/test/socket/temp.file"
static int sockfd = 0;
static struct sockaddr_un servaddr,cliaddr;
int main(int argc, const char *argv[])
{
int ret = 0;
int connfd = 0;
sockfd = socket(AF_UNIX,SOCK_STREAM,0);
if(sockfd < 0 )
{
dbg_printf("socket error \n");
return -1;
}
if(access(UNIX_SOCKET_FILE,F_OK) == 0)
{
dbg_printf("remove unix socket file \n");
remove(UNIX_SOCKET_FILE);
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path,UNIX_SOCKET_FILE);
if(0 != bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)))
{
dbg_printf("bind fail \n");
close(sockfd);
return -1;
}
if(0 != listen(sockfd,10))
{
dbg_printf("linsten fail \n");
close(sockfd);
return -1;
}
int clilen = sizeof(cliaddr.sun_family)+strlen(cliaddr.sun_path);
/*
if((connfd = accept(sockfd,(struct sockaddr *)&cliaddr,(socklen_t *)&clilen)) == -1)
{
dbg_printf("accept fail \n");
return -1;
}
dbg_printf("accept success connfd = %d \n",connfd);
*/
char recvbuf[64];
int n = 0;
int epfd;
epfd = epoll_create(100);
struct epoll_event ev;
ev.events= EPOLLIN;
ev.data.fd = sockfd;
//将sockfd 添加到 监控事件的表中
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
int datafd = 0;
int i = 0;
struct epoll_event evrdy[100];
while(1)
{
ret = epoll_wait(epfd,evrdy,100,-1);
dbg_printf("ret = %d ",ret);
for(i = 0;i < ret; i++)
{
if(evrdy[i].data.fd == sockfd) //建立连接的文件描述符
{
dbg_printf("build new connect \n");
datafd = accept(sockfd,(struct sockaddr *)&cliaddr,&clilen);
ev.events = EPOLLIN;
ev.data.fd = datafd;
//将sockfd 添加到 监控事件的表中
epoll_ctl(epfd,EPOLL_CTL_ADD,datafd,&ev);
}
else //进程间通信描述符
{
dbg_printf("recv data \n");
memset(recvbuf,0,sizeof(recvbuf));
n = recv(evrdy[i].data.fd,recvbuf,sizeof(recvbuf),0);
if(n == -1)
{
dbg_printf("recv error \n");
}
else if(n == 0)
{
epoll_ctl(epfd,EPOLL_CTL_DEL,evrdy[i].data.fd,NULL);
close(evrdy[i].data.fd);
dbg_printf("client exit \n");
}
else if(n > 0)
{
printf("service recvbuf is %s \n",recvbuf);
send(evrdy[i].data.fd,"ok",strlen("ok"),0);
}
}
}
}
close(sockfd);
return 0;
}
client端
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<sys/un.h>
#define dbg_printf(...) printf("[%s.%d]",__func__,__LINE__);printf(__VA_ARGS__)
#define UNIX_SOCKET_FILE "/home/linux/test/socket/temp.file"
static int sockfd = 0;
static struct sockaddr_un servaddr,cliaddr;
int main(int argc, const char *argv[])
{
int ret = 0;
int connfd = 0;
sockfd = socket(AF_UNIX,SOCK_STREAM,0);
if(sockfd < 0 )
{
dbg_printf("socket error \n");
return -1;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sun_family = AF_UNIX;
strcpy(servaddr.sun_path,UNIX_SOCKET_FILE);
int serlen = sizeof(servaddr.sun_family)+strlen(servaddr.sun_path);
if(connect(sockfd,(struct sockaddr *)&servaddr,serlen) == -1)
{
dbg_printf("connect fail \n");
close(sockfd);
return -1;
}
dbg_printf("conect success \n");
char recvbuf[64];
char sendbuf[64];
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
memset(sendbuf,0,sizeof(sendbuf));
strcpy(sendbuf,"i love you");
send(sockfd,sendbuf,strlen(sendbuf),0);
recv(sockfd,recvbuf,sizeof(recvbuf),0);
dbg_printf("recv is %s \n",recvbuf);
sleep(5);
}
close(sockfd);
return 0;
}
实例测试:
编译上面文件为service 和client
client 我修改 发送内容 hello world 和 i love you ,编译生成client1 和client2,客户端与service通信, 测试如下