- 基础概念,什么是多路复用
多路:指多个socket网络连接
复用:指的是复用一个线程
多路复用目前有三种技术:select ,poll ,epoll。其中epoll是最新的也是目前最好的多路复用的技术
- poll实现IO多路复用的特点
相比较于select来说,扩大了对文件描述符的检测的限制,可以由程序员自己设置(极限要根据系统配置)
每次由用户态拷贝至内核态不再重新构造文件描述符表,但是依旧需要不断地拷贝数据,效率很低
- 本次实现目的
完成多个客户端向服务点的链接以及相互通信(TCP)
- 具体相关函数
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参p数:
struct pollfd *fds
关心的文件描述符数组struct pollfd fds[N];
nfds:个数
timeout: 超时检测
毫秒级的:如果填1000,1秒
如果-1,阻塞
struct pollfd {
int fd; /* 检测的文件描述符 */
short events; /* 检测事件 */
short revents; /* 调用poll函数返回填充的事件,poll函数一旦返回,将对应事件自动填充结构体这个成员。只需要判断这个成员的值就可以确定是否产生事件 */
};
事件: POLLIN :读事件
POLLOUT : 写事件
POLLERR:异常事件
代码如下:
服务器端:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <poll.h>
int main(int argc, char const *argv[])
{
if (argc != 2)
{
printf("please input %s <port>\n", argv[0]);
return -1;
}
int sockfd, acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err.");
return -1;
}
printf("socket ok %d\n", sockfd);
struct sockaddr_in serveraddr, caddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(argv[1]));
//serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //0.0.0.0 自动获取主机ip
// serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_addr.s_addr = INADDR_ANY;
socklen_t len = sizeof(caddr);
//&serveraddr -->struct sockaddr_in *
if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
perror("bind err.");
return -1;
}
printf("bind ok.\n");
if (listen(sockfd, 5) < 0)
{
perror("listen err.");
return -1;
}
printf("listen ok.\n");
struct pollfd fds[20]={}; //大小自己确定,没有限定个数
fds[0].fd = 0;
fds[0].events=POLLIN;//读事件
fds[1].fd=sockfd;
fds[1].events=POLLIN;
int n = 2, ret;
char buf[128];
int recvbyte;
while (1)
{
ret = poll(fds, n, -1);
if (ret < 0)
{
perror("poll err.");
return -1;
}
//处理事件
for (int i = 0; i < n; i++)
{
if (fds[i].revents == POLLIN)
{
if (fds[i].fd == 0)
{
fgets(buf, sizeof(buf), stdin);
printf("key:%s\n", buf);
for(int j=2;j<n;j++)
{
send(fds[j].fd,buf,sizeof(buf),0);
}
}
else if (fds[i].fd == sockfd)
{
acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (acceptfd < 0)
{
perror("accept err.");
return -1;
}
printf("accept ok.\n");
printf("ip:%s ,port:%d\n",
inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
fds[n].fd=acceptfd;
fds[n].events=POLLIN;
n++;
}
else
{
recvbyte = recv(fds[i].fd, buf, sizeof(buf), 0);
if (recvbyte < 0)
{
perror("recv err.");
// return -1;
}
else if (recvbyte == 0)
{
printf("%d client exit.\n",fds[i].fd);
close(fds[i].fd);
fds[i]=fds[n-1];
n--;
i--;
break;
}
else
{
printf("%d buf:%s\n",fds[i].fd, buf);
}
}
}
}
}
close(sockfd);
return 0;
}
客户端:
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, const char *argv[])
{
int sockfd, acceptfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("socket err");
return -1;
}
printf("socket OK!\n");
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = inet_addr("192.168.50.247");//根据网络自己设置
if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("connect err");
return -1;
}
printf("connect OK!\n");
pid_t pid = fork();
if (pid < 0)
{
perror("fork err");
return -1;
}
else if (pid == 0)
{
char buf[123]="";
while (1)
{
scanf("%s", buf);
send(sockfd, buf, 32, 0);
}
}
else
{
char buf1[32] = "";
int recvfd;
while (1)
{
recvfd = recv(sockfd, buf1, 32, 0);
if (recvfd < 0)
{
perror("recv err");
return -1;
}
else
{
printf("%s\n", buf1);
}
}
wait(NULL);
}
return 0;
}