C++基于select和epoll的TCP服务器

select版本

服务器

cpp 复制代码
#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;
}

客户端

cpp 复制代码
#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

服务器

cpp 复制代码
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <string>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
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|EPOLLET;
    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;
                }
                int attr=fcntl(cfd,F_GETFL);//获取文件描述符的属性
                attr|=O_NONBLOCK;//用位等于给文件添加属性
                fcntl(cfd,F_SETFL,attr);//添加属性非阻塞
                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|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
            }
            else//判断是否有满足条件的通信套接字
            {
                char buf[5] = {'\0'};
                while(1)
                {
                    int num = recv(curfd, buf, 5, 0);
                    if (num==-1)
                    {
                        if(errno==EAGAIN)
                        {
                            printf("数据读取完毕");
                        }
                        else
                        {
                            perror("recv error");
                        }
                        break;
                    }
                    else if(num==0)
                    {
                        printf("连接断开\n");
                        epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                        close(curfd);
                        break;
                    }
                    else
                    {
                        printf("从客户端收到数据:%s\n",buf);
                        char sendbuf[1024]={"服务器收到了客户端的数据:"};
                        strcat(sendbuf,buf);
                        flag = send(curfd, sendbuf,strlen(sendbuf), 0);
                        if (flag == -1)
                        {
                            perror("send error");
                        }
                        memset(sendbuf, 0, 1024);
                    }
                    memset(buf, 0, 5);
                }
            }
        }
    }
    close(sfd);
    return 0;
}
相关推荐
多多*5 分钟前
微服务网关SpringCloudGateway+SaToken鉴权
linux·开发语言·redis·python·sql·log4j·bootstrap
梓仁沐白5 分钟前
【Kotlin】协程
开发语言·python·kotlin
Cyanto25 分钟前
Java并发编程面试题
java·开发语言·面试
海的诗篇_32 分钟前
前端开发面试题总结-JavaScript篇(一)
开发语言·前端·javascript·学习·面试
光芒Shine32 分钟前
【物联网-TCP/IP】
网络·网络协议·tcp/ip
m0_5522008237 分钟前
《UE5_C++多人TPS完整教程》学习笔记37 ——《P38 变量复制(Variable Replication)》
c++·游戏·ue5
小wanga40 分钟前
【递归、搜索与回溯】专题三 穷举vs暴搜vs回溯vs剪枝
c++·算法·机器学习·剪枝
じ☆ve 清风°41 分钟前
理解JavaScript中map和parseInt的陷阱:一个常见的面试题解析
开发语言·javascript·ecmascript
sss191s1 小时前
Java 集合面试题从数据结构到 HashMap 源码剖析详解及常见考点梳理
java·开发语言·数据结构
IGP91 小时前
20250606-C#知识:委托和事件
开发语言·c#