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;
}