• Linux 网络编程之 Select


    /*server*/
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/time.h>
    #include <stdlib.h>
    #include "common.h"
    #include <iostream>
    void process_cli(Client *client, char* recvbuf, int len,int count);  //客户请求处理函数
    int main(int argc,char * argv[])
    {
        int listenFd=0;
        //地址信息结构体大小
        if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {    //调用socket创建用于监听客户端的socket
            std::cout<<"Creating socket failed."<<std::endl;
            exit(1);
        }
    
        int opt = SO_REUSEADDR;
        setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));    //设置socket属性
    
        struct sockaddr_in serverAddr;     //服务器地址信息结构体
        bzero(&serverAddr,sizeof(serverAddr));
        serverAddr.sin_family=AF_INET;
        serverAddr.sin_port=htons(PORT);
        serverAddr.sin_addr.s_addr = htonl (INADDR_ANY);
        if (bind(listenFd, (struct sockaddr *)&serverAddr, sizeof(struct sockaddr)) == -1) {    //调用bind绑定地址
            std::cout<<"Bind error."<<std::endl;
            exit(1);
        }
    
        if(listen(listenFd,BACKLOG) == -1) {
            //调用listen开始监听
            std::cout << "listen() error"<<std::endl;
            exit(1);
        }
    
    
     
        Client client[FD_SETSIZE];     //FD_SETSIZE为select函数支持的最大描述符个数
        char recvbuf[MAXDATASIZE];    //缓冲区
        int sin_size=sizeof(struct sockaddr_in);
        //初始化select
        int maxI;
        int maxFd;
        int sockfd;
        int    nReady;
        ssize_t      n;
        fd_set readSet;//可读socket的文件描述符的集合
        fd_set allSet;//所有socket的文件描述符的集合
        for (int i = 0; i < FD_SETSIZE; i++) {
            client[i].fd = -1;
        }
        maxFd= listenFd;
        FD_ZERO(&allSet);        //清空
        FD_SET(listenFd, &allSet);    //将监听socket加入select检测的描述符集合
    
        int connectFd=0;     //socket文件描述符
        int clientCount=0;
        while(1)
        {
            struct sockaddr_in addr;
            readSet = allSet;
            nReady = select(maxFd+1, &readSet, NULL,NULL, NULL);    //调用select
            if (FD_ISSET(listenFd, &readSet)) {      //检测是否有新客户端请求
                //调用accept,返回服务器与客户端连接的socket描述符
                if ((connectFd = accept(listenFd,(struct sockaddr *)&addr,(socklen_t *)&sin_size))==-1) {
                    std::cout<<"accept() error"<<std::endl;
                    continue;
                }
    
                //将新客户端的加入数组
                int i = 0;
                for (int i = 0; i < FD_SETSIZE; i++) {
                    if (client[i].fd < 0) {
                        client[i].fd = connectFd;    //保存客户端描述符
                        char addrBuf[32];
                        sprintf(addrBuf,"%s:%d",inet_ntoa(addr.sin_addr),addr.sin_port);
                        clientCount++;
                        client[i].m_addr=std::string(addrBuf);
                        break;
                    }
                }
                if (i == FD_SETSIZE)
                {
                    std::cout<<"too many clients"<<std::endl;
                }
                if (i > maxI)          //数组最大元素值
                {
                    maxI = i;
                }
                FD_SET(connectFd, &allSet);   //将新socket连接放入select监听集合
                if (connectFd > maxFd)
                {
                    maxFd = connectFd;   //确认maxfd是最大描述符
                }
    
                if (--nReady <= 0)
                {
                    continue;      //如果没有新客户端连接,继续循环
                }
            }
    
            for (int i = 0; i <= maxI; i++) 
            {
                if ( (sockfd = client[i].fd) < 0)       //如果客户端描述符小于0,则没有客户端连接,检测下一个
                { 
                    continue;
                }
                if (FD_ISSET(sockfd, &readSet)) 
                {        //检测此客户端socket是否有数据
                    int nReadLength = recv(sockfd, recvbuf, MAXDATASIZE,0);
                    if (nReadLength == 0) 
                    { //从客户端socket读数据,等于0表示网络中断
                        close(sockfd);        //关闭socket连接
                        FD_CLR(sockfd, &allSet);    //从监听集合中删除此socket连接
                        client[i].fd = -1;        //数组元素设初始值,表示没客户端连接
                        std::cout<<"Client Addr: "<< client[i].m_addr << " Close "<<std::endl;
                        clientCount--;
                    } 
                    else
                    {
                        process_cli(&client[i], recvbuf, nReadLength,clientCount);    //接收到客户数据,开始处理
                    }
                    if (--nReady <= 0)
                    {
                        break;       //如果没有新客户端有数据,跳出for循环回到while循环
                    }  
                }
            }
        }
        close(listenFd);  //关闭服务器监听socket
    }
    
    void process_cli(Client *client, char* recvbuf, int len,int count)
    {
        char sendbuf[MAXDATASIZE];
        OctArray16 * recvData = (OctArray16*)recvbuf;
        std::cout<<"RecvData From Client:"<< client->m_addr <<"   Data:"<<recvData->toString()<<std::endl;
        sprintf(sendbuf,"%d--%s",count,GetCurrentTime().c_str());
        OctArray16 sendData(sendbuf);
        send(client->fd,&sendData,sendData.size(),0);
    }
    
    
    /* client */
    #include <stdio.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <unistd.h>
    #include <string.h>
    #include <arpa/inet.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <iostream>
    #include "common.h"
    
    
    int main(int argc, char *argv[])
    {
        char hostname[100];
        gethostname(hostname,sizeof(hostname));
        int sockfd, numbytes;
    
        struct hostent *he;
        struct sockaddr_in serv_addr;
        if ((he = gethostbyname(defaultHostName)) == NULL)
        {
            std::cout<<"gethostbyname error!"<<std::endl;
            return 0;;
        }
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            std::cout<< "socket error!"<<std::endl;
            return 0;
        }
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(PORT);
        serv_addr.sin_addr = *((struct in_addr *)he->h_addr);
        bzero(&(serv_addr.sin_zero), 8);
        if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
        {
            std::cout<<"connect error!"<<std::endl;
            return 0;
        }
        pid_t pId= getpid();
        char SendBuf[16];
    
        srand(time(0));
        char buf[MAXDATASIZE];
        for(int x = 0 ; x < 10 ; x++) {
            sleep(rand()%10);
            sprintf(SendBuf,"%d",pId);
            OctArray16 sendData(SendBuf);
            if ((numbytes = send(sockfd,&sendData,sendData.size(),0)) != -1)
            {
                if ((numbytes = read(sockfd, buf, MAXDATASIZE)) == -1)
                {
                    std::cout<<"read error!
    "<<std::endl;
                    return 0;
                }
                OctArray16 * RecvData = (OctArray16*)buf;
                std::cout<<"Client  "<< pId <<"  Recv: "<<RecvData->toString()<<std::endl;
            }
        }
        close(sockfd);       
    }
    
    /*common.h*/
    #ifndef COMMON_H_
    #define COMMON_H_
    const int PORT=2248;           //服务器端口
    const int BACKLOG=5;           //listen队列中等待的连接数
    const int MAXDATASIZE=1024;    //缓冲区大小
    const char defaultHostName[] = "127.0.0.1";
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <iostream>
    #include <time.h>
    #include <string.h>
    struct Client {       //客户端结构体
        int fd;        //客户端socket描述符
        std::string m_addr;
    };
    class OctArray16 {
    public:
        uint32_t   m_len;
        char     data[28];
        size_t size(){
            return 32;
        }
        std::string toString(){
            return std::string(data,m_len);
        }
        
        OctArray16(const char *str){
            m_len = strlen(str);
            strcpy(data,str);
        }
    };
    
    std::string GetCurrentTime(){
        time_t cutTime=time(0);
        tm * tmT=localtime(&cutTime);
        char buf[12];
        sprintf(buf,"%d:%d:%d",tmT->tm_hour,tmT->tm_min,tmT->tm_sec);
        return std::string(buf);
    }
    #endif
    
    cmake_minimum_required(VERSION 2.8)
    
    ## Use the variable PROJECT_NAME for changing the target name
    set( PROJECT_NAME "SelectLearn" )
    
    ## Set our project name
    project(${PROJECT_NAME})
    
    ## Use all the *.cpp files we found under this folder for the project
    SET(CLIENT_SOURCE Client.cpp common.h)
    SET(SERVER_SOURCE Server.cpp common.h)
    ## Define the executable
    add_executable(server ${SERVER_SOURCE})
    add_executable(client ${CLIENT_SOURCE})
    
  • 相关阅读:
    Enterprise Library系列文章回顾与总结
    .NET设计模式系列文章
    从Google趋势看.NET下的Ajax框架
    Atlas学习手记(18):使用DragPanel实现拖放面板
    Atlas学习手记(2):全面了解ScriptManager
    .NET设计模式(17):命令模式(Command Pattern)
    Atlas学习手记(3):由UpdatePanel开始
    Atlas学习手记(16):使用PasswordStrength检测密码强度
    Atlas学习手记(17):使用FilteredTextBox过滤字符
    用Windows Live Writer在博客园发布Post
  • 原文地址:https://www.cnblogs.com/Dennis-mi/p/7866414.html
Copyright © 2020-2023  润新知