1、lfd = socket()建立监听套接字
2、setsockopt() 允许端口复用
3、bind() 给监听套接字绑定sockaddr地址结构
4、listen() 设置监听上限
5、ret = poll( &pfds) 小秘书只负责监听
6、对ret分类判断 解析读事件(都由谁产生) 就这2种:(新客户端想建立连接 + 旧客户端想发数据给服务器)
7、poll比select优化 遍历:哪个文件描述符产生读事件更高效 if (pfds[i].revents & POLLIN)
8、先分析pfds[0].revents 有无新客户端建立连接
9、新的客户端对应文件描述符加入监听数组中
10、更新监听文件描述符数组的有效个数
8、再分析其他的pfds[i].revents有无数据到达
9、旧客户端(已经建立连接的)要发数据给服务器
10、读数据
11、模拟数据处理
12、处理后的数据发给客户端
#include "wrap.h"
#include <arpa/inet.h>
#include <sys/time.h>
#include <ctype.h>
#include <poll.h>
#define PORT 9527
#define MAXLISTEN 128
int main(void)
{
int lfd, cfd; // 监听 + 通信 套接字描述符
int ret;
int n; //读到的字节数
char buf[BUFSIZ]; // 接收读到的数据
struct sockaddr_in clientaddr;
socklen_t clientaddrlen = sizeof(clientaddr); // 用于记录连接到服务器的客户端的ip + 端口号
// 1、创建监听套接字
lfd = Socket(AF_INET, SOCK_STREAM, 0);
// 2、设置端口复用
int opt = 1;
ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
if (ret == -1)
sys_error("setsockopt error");
// 3、绑定地址结构
struct sockaddr_in saddr;
socklen_t saddrlen = sizeof(saddr);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT); // 端口号转成网络字节序
saddr.sin_addr.s_addr = htonl(INADDR_ANY); // ip地址转成网络字节序
Bind(lfd, (struct sockaddr *)&saddr, saddrlen);
// 4、设置监听上限
Listen(lfd, MAXLISTEN);
// 5、poll小秘书只负责监听(1次调用, 监听1次)
// 定义1个监听的文件描述符数组====代替select的监听读集合
struct pollfd pfds[1024];
pfds[0].fd = lfd;
pfds[0].events = POLLIN;
pfds[0].revents = 0; // 只监听lfd的读事件(有无新客户端建立连接)
int nfds = 1; // 监听描述符数组中实际有效的文件描述符个数
while (1)
{
ret = poll(pfds, nfds, 0); // 非阻塞轮询式监听
if (ret > 0) // 6、分析监听数组中哪几个文件描述符发生了读事件
{
// 7、先分析pfds[0].revents 有无新客户端建立连接
if (pfds[0].revents & POLLIN)
{
cfd = Accept(lfd, (struct sockaddr *)&clientaddr, &clientaddrlen);
// 打印客户端信息
char cip[256];
inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, cip, sizeof(cip));
printf("新连接的客户端: ip %s port %d\n", cip, ntohs(clientaddr.sin_port));
// 8、新的客户端对应文件描述符加入监听数组中
pfds[nfds].fd = cfd;
pfds[nfds].events = POLLIN;
pfds[nfds].revents = 0;
// 9、更新监听文件描述符数组的有效个数
++nfds;
}
// 7、再分析其他的pfds[i].revents有无数据到达
for (int i = 1; i < nfds; ++i)
{
if (pfds[i].revents & POLLIN)
{
// 8、旧客户端(已经建立连接的)要发数据给服务器
// 9、读数据
if ((n = read(pfds[i].fd, buf, BUFSIZ)) > 0) // 真有数据到达
{
// 10、模拟数据处理
for (int j = 0; j < n; ++j)
buf[i] = toupper(buf[i]);
// 11 、处理后的数据发给客户端
write(pfds[i].fd, buf, n);
}
else if (n == 0) // 旧客户端关闭连接
{
// 10、服务器也关闭连接
Close(pfds[i].fd);
// 11、该文件描述符剔除出去(从监听数组中)
// 数组整体前移
// --nfds; // 更新监听数组的有效文件描述符个数
}
}
}
}
else if (ret == -1)
sys_error("poll error");
}
return 0;
}