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

服务器

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咩咩大主教

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值