首页 > 编程语言 >C++基于select和epoll的TCP服务器

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

时间:2024-09-18 22:21:53浏览次数:12  
标签:perror epoll int C++ flag TCP error sfd include

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

标签:perror,epoll,int,C++,flag,TCP,error,sfd,include
From: https://blog.csdn.net/weixin_74027669/article/details/142304550

相关文章

  • C++入门基础知识75(高级)——【关于C++ Web 编程】
    成长路上不孤单......
  • ModbusTCP报文详解
    ModbusTCP与ModbusRtu(ASCI)数据帧的区别总结:ModbusTCP就是在ModbusRtu(ASCI)基础上去掉CRC,再加上六个0一个6ModbusTCPMBAP报文头域长度描述客户机服务器事务处理标识符2字节Modbus请求/响应事务处理的识别客户机启动服务器从接收的请求中重新复制协议标识符2字节0=M......
  • c++运算符
    #include<iostream>//运算符usingnamespacestd;intmain(){inta=9;intb=6;cout<<"这是加法:"<<a+b<<endl;cout<<"这是减法:"<<a-b<<endl;cout<<"这是乘法:"<<......
  • C/C++中哪些数据存放于栈区、堆区、静态区、常量区的详细说明
    文章目录1.栈区(Stack)2.堆区(Heap)3.静态区(Static)4.常量区(Read-OnlyorConstantSection)总结:栈(Stack)的生长:堆(Heap)的生长:测试一答案二答案1.栈区(Stack)栈区用于存放局部变量和函数调用信息,它的内存由编译器自动分配和释放,具有自动管理的特点。栈区的内存分配遵循先......
  • 【Linux】多路转接epoll
    一、I/O多路转接poll1.1poll函数接口函数原型函数参数fds:是一个poll函数监听的结构列表,每一个元素中包含了三部分内容:文件描述符,监听的事件集合,返回的事件集合。nfds:表示的是fds数组的长度timeout:表示poll函数的超时时间,单位是毫秒函数功能       poll函数用......
  • c++primer第七章函数学习笔记
    函数的基本知识定义函数无返回值voidfunctionName(parameterList){  statement(s);  return;//optional}有返回值  typeNamefuntionName(parameterList)  {    statements;    returnvalue;  }#include<iostream>usi......
  • 南沙C++信奥老师解一本通题:1337:【例3-2】单词查找树
    ​【题目描述】在进行文法分析的时候,通常需要检测一个单词是否在我们的单词列表里。为了提高查找和定位的速度,通常都画出与单词列表所对应的单词查找树,其特点如下:1.根结点不包含字母,除根结点外每一个结点都仅包含一个大写英文字母;2.从根结点到某一结点,路径上经过的字母依次连起......
  • java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not fo
    一、概述在AndroidStudio中集成opencv,使用其native函数时报的一个错误 二、解决办法opencv提供的native文件夹中提供的是动态库但是在gradle中的配置中,配置的stl是静态的,将其改为shared就行了externalNativeBuild{cmake{cp......
  • C++ vector 列表初始化
    vector<int>vl(10);//v1有10个元素,每个的值都是0vector<int>v2{10};//v2有1个元素,该元素的值是10vector<int>v3(10,1);//v3有10个元素,每个的值都是1vector<int>v4{10,1};//v4有2个元素,值分别是10和1如果初始化时使用了花括号的形式但是提供的值又不能......
  • 【C++】多态的认识和理解
    个人主页文章目录⭐一、多态的概念......