自适应网站做推广,做网站找我要服务器密码,注册的网站,网站建设 网站优化//head.h 头文件 //TcpGrpSer.c 服务器端 //TcpGrpUsr.c 客户端 通过IO多路复用实现服务器在单进程单线程下可以与多个客户端交互 API epoll函数 #includesys/epoll.h
int epoll_create(int size);
功能#xff1a;创建一个epoll句柄//创建红黑树根… //head.h 头文件 //TcpGrpSer.c 服务器端 //TcpGrpUsr.c 客户端 通过IO多路复用实现服务器在单进程单线程下可以与多个客户端交互 API epoll函数 #includesys/epoll.h
int epoll_create(int size);
功能创建一个epoll句柄//创建红黑树根节点
epoll把要监测的事件文件描述符挂载到红黑树上
参数size 没有意义但是必须0
返回值成功返回根节点对应的文件描述符失败返回-1int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:实现对于epoll的控制
参数
epfd:epoll_create创建的句柄
op控制方式EPOLL_CTL_ADD:添加要监测的事件文件描述符EPOLL_CTL_MOD修改epoll检测的事件类型EPOLL_CTL_DEL将文件描述符从epoll删除
fd:要操作的文件描述符
event:事件结构体
typedef union epoll_data {void *ptr;int fd;//使用这个uint32_t u32;uint64_t u64;} epoll_data_t;struct epoll_event {uint32_t events; //EPOLLIN(读) EPOLLOUT写epoll_data_t data; /* User data variable */};
返回值成功返回0失败返回-1int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能阻塞等待准备好的文件描述符
参数
epfd:epoll句柄
events:存放就绪事件描述符的结构体数组首地址
maxevents:监听的事件最大个数
timeout:超时检测0:毫秒级检测0立即返回-1不关心是否超时返回值
0:准备好的文件描述符的个数
0超时
0:失败head.h
#ifndef __HEAD_H__
#define __HEAD_H__#includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includemath.h
#includeerrno.h
#includefcntl.h
#includesignal.h#includesys/stat.h
#includesys/wait.h
#includesys/types.h
#includesys/ipc.h
#includesys/msg.h
#includesys/shm.h
#includesys/time.h
#includesys/sem.h#includepthread.h
#includesemaphore.h#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.h
#includesys/select.h
#includepoll.h
#includesys/epoll.h
#includesys/fcntl.h#define NUM 10
#define ERR_MSG(msg) \do \{ \printf(line: %d\n, __LINE__); \perror(msg); \} while (0)#define PORT 6666 // 端口号的网络字节序 102449151
#define IP 192.168.250.100 // ifconfig查看本机IP (ipv4)#endifTcpGrpSer.c
#include head.hint main(int argc, const char *argv[])
{// 创建流式套接字int sfd socket(AF_INET, SOCK_STREAM, 0);if (sfd 0){ERR_MSG(socket);return -1;}// 填充服务器自身的地址信息结构体// 真实的地址信息结构体根据地址族制定AF_INET ;struct sockaddr_in sin;sin.sin_family AF_INET; // 必须填充AF_INETsin.sin_port htons(PORT); // 端口号的网络字节序 102449151sin.sin_addr.s_addr inet_addr(IP); // ifconfig查看本机IPint reuse 1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, reuse, sizeof(reuse)) 0) // 允许端口快速被重复使用{ERR_MSG(setsockopt);return -1;}// 绑定连接if (bind(sfd, (struct sockaddr *)sin, sizeof(sin)) 0){ERR_MSG(bind);return -1;}printf(bind success\n);// 设置监听if (listen(sfd, 128) 0){ERR_MSG(listen);return -1;}struct epoll_event event;struct epoll_event events[10]; // 存放就绪事件描述符的数组char buf[128] {0};// 创建epoll句柄int epfd epoll_create(1);if (epfd 0){ERR_MSG(epoll_create);exit(-1);}// 添加准备就绪事件进入epoll;event.events EPOLLIN; // 读事件event.data.fd sfd; // 监听套接字放入列表if (epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, event) 0){ERR_MSG(epoll_ctl);exit(-1);}event.events EPOLLIN; // 读事件event.data.fd 0; // 终端输入套接字放入列表if (epoll_ctl(epfd, EPOLL_CTL_ADD, 0, event) 0){ERR_MSG(epoll_ctl);exit(-1);}// 监听事件是否发生int s_res 0;struct sockaddr_in cin;socklen_t len sizeof(cin);struct sockaddr_in savcin[1024];int flags[1024] {0};int newfd -1;ssize_t res 0;while (1){// 监测文件描述符是否准备就绪// 如果成功s_res接收返回的事件个数就绪的事件存储到events数组中s_res epoll_wait(epfd, events, 10, -1);if (s_res 0){ERR_MSG(select);return -1;}// 与客户端通信for (int i 0; i s_res; i){if (events[i].events EPOLLIN){if (events[i].data.fd sfd){printf(客户端连技事件\n);newfd accept(sfd, (struct sockaddr *)cin, len);if (newfd 0){perror(accept);return -1;}savcin[newfd] cin;flags[newfd] 1;printf([%s:%d] 客户端连接成功 newfd %d __%d__ \n,inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), newfd, __LINE__);event.events EPOLLIN | EPOLLET; // 读事件event.data.fd newfd; // 新套接字放入链表if (epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, event) 0){ERR_MSG(epoll_ctl);exit(-1);}}else if (0 events[i].data.fd){printf(触发键盘输入事件\n);int sndfd-1;resscanf(%d %s,sndfd,buf);while(getchar() !10);if(res!2){printf(请输入正确数据格式[fd(4~1023)] string\n);continue;}//判断文件是否合法if(flags[i]){printf(sndfd %d 是非法文件描述符\n,sndfd);continue;}if(send(sndfd,buf,sizeof(buf),0)0){ERR_MSG(send);}bzero(buf,sizeof(buf));}else{printf(客户端交互事件\n);// res scanf(%s, buf);// while (getchar() ! 10)res recv(events[i].data.fd, buf, sizeof(buf), 0);if (res 0){ERR_MSG(send);return -1;}else if (0 res){printf([%s:%d] 客户端下线 newfd %d __%d__ \n,inet_ntoa(savcin[i].sin_addr), ntohs(savcin[i].sin_port), events[i].data.fd, __LINE__);close(i); // 关闭文件描述符flags[i] 0;}printf([%s:%d] 客户端 newfd %d : %s, __%d__ \n,inet_ntoa(savcin[i].sin_addr), ntohs(savcin[i].sin_port), events[i].data.fd, buf, __LINE__);}}}}if (close(sfd) 0){ERR_MSG(close);return -1;}return 0;
}TcpGrpUsr.c
#include head.hint main(int argc, const char *argv[])
{//创建流式套接字int sfd socket(AF_INET,SOCK_STREAM,0);if(sfd0){ERR_MSG(socket);return -1;}int reuse 1;if(setsockopt(sfd, SOL_SOCKET,SO_REUSEADDR,reuse,sizeof(reuse)) 0) //允许端口快速被重复使用{ERR_MSG(setsockopt);return -1;}//填充服务器自身的地址信息结构体//真实的地址信息结构体根据地址族制定AF_INET ; man 7 ipstruct sockaddr_in sin; sin.sin_family AF_INET; //必须填充AF_INETsin.sin_port htons(PORT); //端口号的网络字节序 102449151sin.sin_addr.s_addr inet_addr(IP); //ifconfig查看本机IPif(connect(sfd,(struct sockaddr *)sin,sizeof(sin))0){perror(connect);return -1;}printf(连接成功\n);//创建集合struct pollfd fds[2];fds[0].fd 0;fds[0].events POLLIN;fds[1].fd sfd;fds[1].events POLLIN;char buf[128];int res0;while(1){//阻塞方式监测集合res poll(fds,2,-1);if(res 0){ERR_MSG(poll);return -1;}else if(0 res){printf(time out...\n); //超时break;}//判断0文件描述符是否右POLLIN事件if((fds[0].revents POLLIN)){fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] 0;if(send(sfd,buf,sizeof(buf),0) 0){ERR_MSG(send);return -1;}printf(发送成功\n);}//判断sfd文件描述符是否右POLLIN事件if(fds[1].revents POLLIN){//接收数据bzero(buf,sizeof(buf));res recv(sfd,buf,sizeof(buf),0);if(res0){ERR_MSG(recv);return -1;}else if(res 0){printf([%s:%d] 服务器下线__%d__ \n,\inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),__LINE__);break;}printf([%s:%d] cfd %d : %s__%d__ \n,\inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),sfd,buf,__LINE__);} }if(close(sfd)0){ERR_MSG(close);return -1;}return 0;
}