C++基于select和epoll的TCP服务器
select版本
服务器
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string>
#include <pthread.h>
#include <sys/select.h>
#include <stdio.h>
int main()
{int flag=0;struct sockaddr_in saddr;saddr.sin_port = htons(8999);saddr.sin_addr.s_addr = INADDR_ANY;saddr.sin_family = AF_INET;int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1){perror("socket error");return 1;}int optvalue;setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optvalue,sizeof(int));//设置端口复用flag = bind(sfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in));if (flag == -1){perror("bind error");return 1;}flag=listen(sfd, 100);if(flag==-1){perror("listen error");return 1;}fd_set readset,tmp;FD_ZERO(&readset);FD_SET(sfd,&readset);int nfds=sfd;while (1){tmp=readset;//每一次tmp的值都会被内核修改,所以要用readset重制flag=select(nfds+1,&tmp,NULL,NULL,NULL);//异常和写集合一般不检测if(flag==-1){perror("select error");continue;}for(int i=sfd;i<=nfds;i++)//i可以从0开始,但是会做几次多余判断{int port;if(i==sfd&&FD_ISSET(i,&tmp))//判断监听套接字是否满足条件{struct sockaddr_in caddr;int clen=sizeof(caddr);int cfd = accept(sfd, (struct sockaddr*)&caddr, (socklen_t*)&clen);port=ntohs(caddr.sin_port);if (cfd == -1){perror("accept error");continue;}char ip[1024]={'\0'};//必须加上,不然解析IP会失败printf("接收到了客户端%s:%d的连接\n",inet_ntop(AF_INET,&caddr.sin_addr,ip,1024),port);FD_SET(cfd,&readset);//将通信套接字加入文件描述符表nfds=nfds>cfd?nfds:cfd;//更新ndfs}else//判断是否有满足条件的通信套接字{if(FD_ISSET(i,&tmp)){char buf[1024] = {'\0'};flag = recv(i, buf, 1024, 0);if (flag==-1){perror("read error");break;}else if(flag==0){printf("连接断开\n");FD_CLR(i,&readset);close(i);}else{printf("从客户端收到数据:%s\n",buf);std::string sendstr="服务器收到了"+std::to_string(port)+"的数据";flag = send(i, sendstr.c_str(), sendstr.length(), 0);if (flag == -1){perror("send error");}memset(buf, 0, 1024);}}}}}close(sfd);return 0;
}
客户端
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string>int main()
{int flag;struct sockaddr_in* caddr=(struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));inet_pton(AF_INET, "192.168.101.231", &caddr->sin_addr.s_addr);caddr->sin_family = AF_INET;caddr->sin_port = htons(8999);int cfd = socket(AF_INET, SOCK_STREAM, 0);if (cfd == -1){perror("socket error");return 1;}int addrlen=sizeof(struct sockaddr);int i=0;flag= connect(cfd, (struct sockaddr*)caddr, addrlen);//caddr就是指针类型if (flag == -1){perror("connect error");return 1;}while(1){char buf[1024] = {'\0'};std::string sendstr="我是客户端"+std::to_string(getpid())+"发送了数据"+std::to_string(i);flag = send(cfd, sendstr.c_str(), sendstr.length(),0);if (flag == -1){perror("send error");continue;}printf("send:%s\n", sendstr.c_str());flag = recv(cfd, buf, 1024, 0);if (flag == -1){perror("recv error");continue;}printf("客户端收到数据:%s\n", buf);memset(buf, 0, 1024);i++;sleep(1);}close(cfd);return 0;
}
epoll
服务器
int main()
{int flag=0;struct sockaddr_in saddr;saddr.sin_port = htons(8999);saddr.sin_addr.s_addr = INADDR_ANY;saddr.sin_family = AF_INET;int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1){perror("socket error");return 1;}int optvalue;setsockopt(sfd, SOL_SOCKET, SO_REUSEPORT, &optvalue,sizeof(int));//设置端口复用flag = bind(sfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in));if (flag == -1){perror("bind error");return 1;}flag=listen(sfd, 100);if(flag==-1){perror("listen error");return 1;}int epfd=epoll_create(1);struct epoll_event ev;ev.data.fd=sfd;ev.events=EPOLLIN;flag=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&ev);struct epoll_event revents[1024];while (1){int num=epoll_wait(epfd,revents,1024,-1);if(num==-1){perror("epoll wait error");continue;}for(int i=0;i<num;i++){int port;int curfd=revents[i].data.fd;if(curfd==sfd)//判断监听套接字是否满足条件{struct sockaddr_in caddr;int clen=sizeof(caddr);int cfd = accept(sfd, (struct sockaddr*)&caddr, (socklen_t*)&clen);port=ntohs(caddr.sin_port);if (cfd == -1){perror("accept error");continue;}char ip[1024]={'\0'};//必须加上,不然解析IP会失败printf("接收到了客户端%s:%d的连接\n",inet_ntop(AF_INET,&caddr.sin_addr,ip,1024),port);ev.data.fd=cfd;ev.events=EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);}else//判断是否有满足条件的通信套接字{char buf[1024] = {'\0'};flag = recv(curfd, buf, 1024, 0);if (flag==-1){perror("recv error");break;}else if(flag==0){printf("连接断开\n");epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);close(curfd);}else{printf("从客户端收到数据:%s\n",buf);std::string sendstr="服务器收到了"+std::to_string(port)+"的数据";flag = send(curfd, sendstr.c_str(), sendstr.length(), 0);if (flag == -1){perror("send error");}memset(buf, 0, 1024);}}}}close(sfd);return 0;
}