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;
}
相关推荐
不想写bug呀15 分钟前
多线程案例——单例模式
java·开发语言·单例模式
bantinghy20 分钟前
Linux进程单例模式运行
linux·服务器·单例模式
我不会写代码njdjnssj1 小时前
网络编程 TCP UDP
java·开发语言·jvm
小和尚同志1 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手1 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
李少兄9 天前
解决OSS存储桶未创建导致的XML错误
xml·开发语言·python
阿蒙Amon9 天前
《C#图解教程 第5版》深度推荐
开发语言·c#
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
AWS官方合作商9 天前
AWS ACM 重磅上线:公有 SSL/TLS 证书现可导出,突破 AWS 边界! (突出新功能的重要性和突破性)
服务器·https·ssl·aws
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发