一、技术说明
1.epoll
多路IO转接服务器也叫做多任务IO服务器。该类服务器实现的主旨思想是,不再由应用程序自己监视客户端连接,取而代之由内核替应用程序监视文件。epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
2.socket编程
采用一个服务器,多个客户端的模式传递数据
3.管道
使用管道来进行父子进程之间的通信
4.fork
创建子进程,父进程和子进程执行不同的事情
5.链表
由于项目存在广播功能,及需要存储所有客户端的fd,然后广播出去,可以采用数组存储,数组上限代表这可以加入的最大客户端数量,或者采用链表存储,本实验由于采用c++编程,底层封装了函数,方便,所以采用链表存储,加入和删除结点方便。
6.make
本实验由于存在多个.c和.h文件,所以采用make编译
二、流程图
服务器
客户端
三、核心代码
1.服务器server
listenfd = socket(AF_INET, SOCK_STREAM, 0);
//AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
//SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
//返回指向新创建的socket的文件描述符
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络地址和端口号。
listen(listenfd, 20);
//典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有20个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen()成功返回0,失败返回-1。
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
//阻塞等待,处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
...
close(sockfd);
2.客户端
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port = htons(SERV_PORT);
Connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//阻塞进程,直到连接建立成功或者出现错误。这种情况通常发生在客户端连接服务器时,因为服务器可能还未启动或者正在处理其他客户端请求。
...
close(sockfd);
3.epoll
efd = epoll_create(OPEN_MAX);
struct epoll_event tep;
tep.events = EPOLLIN;
tep.data.fd = listenfd;
res = epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &tep);
while(1)
{
nready = epoll_wait(efd, ep, OPEN_MAX, -1);
for (i = 0; i < nready; i++)
{
if (ep[i].data.fd == listenfd)
{
...
}
}
}