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})
原文地址:https://www.cnblogs.com/Dennis-mi/p/7866414.html