现实中我们写的很多网络通信的程序都不可能是一对一的,很多时候需要来实现一个并发的服务器来满足更多用户的接入,而对于并发的实现也有好几种方式,多线程、多进程、多路复用。
多路复用
对于多路复用这种方式也有好几种实现方式:poll、epoll、select,虽然用起来有一些不同的细节,不过都大同小异,在我的程序中使用的是select.
先说一下我的需求,其实我只有一个sockfd文件描述符,只是一个udp通信的socket描述符,所以我这里并不是一个并发程序,我使用select机制只是为了使用其中的延时检测,服务器等待指定的时间,程序框架如下
client.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
typedef struct sockaddr* saddrp;
int main(int argc, char const *argv[])
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (0 > sockfd)
{
perror("socket");
return -1;
}
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(5577);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
socklen_t addr_len = sizeof(struct sockaddr_in);
while(1)
{
char buf[255] = {};
printf("Please input the return value:");
fgets(buf,255,stdin);
buf[strlen(buf) - 1] = '\0';
sendto(sockfd,buf,strlen(buf)+1,0,(saddrp)&addr,addr_len);
if (0 == strcmp(buf,"q")) break;
}
close(sockfd);
return 0;
}
客户端就是一个普通的UDP client程序
server.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/time.h>
#include <arpa/inet.h>
typedef struct sockaddr* saddrp;
int main(int argc, char const *argv[])
{
//创建socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if (0 > sockfd)
{
perror("sockfd");
return -1;
}
//准备地址
struct sockaddr_in addr = {};
addr.sin_family = AF_INET;//ipv4
addr.sin_port = htons(5577);//端口号
addr.sin_addr.s_addr = inet_addr("127.0.0.1");//我的ip地址
socklen_t addr_len = sizeof(struct sockaddr_in);
//绑定
int ret = bind(sockfd,(saddrp)&addr,sizeof(addr));
if (0 > ret)
{
perror("bind");
return -1;
}
/* 超时变量 */
struct timeval tv;
fd_set fdsr;
while(1) {
FD_ZERO(&fdsr);
FD_SET(sockfd,&fdsr);
/* 30S等待 */
tv.tv_sec = 30;
tv.tv_usec =0;
/* 监控描述符是sockfd,所以这里最大的个数应该是socket+1,太小的话检测不到 */
int ret = select(sockfd+1, &fdsr, NULL, NULL, &tv);
switch(ret)
{
case 0:
printf("select time out ......\n");
break;
case -1:
perror("select");
break;
default:
if(FD_ISSET(sockfd, &fdsr)) {
char buf[128] = {0};
recvfrom(sockfd,buf,sizeof(buf),0,(saddrp)&addr,&addr_len);
rintf("Recv:%s\n",buf);
}
break;
}
}
close(sockfd);
return 0;
}
将udp server的socket描述符加入select等到,设置的超时时间是30S,如果接收到消息就会执行default部分,打印消息,
结果:超时检测
消息检测