LINUX|深入学习linux socket编程之select

参考:
深入学习linux socket编程之select
(https://www.cnblogs.com/Jason-Damon/archive/2013/04/18/3029385.html)
 select系统调用是用来让我们的程序监视多个文件句柄(file descriptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。
 
Select函数的定义:

#include   
#include   
#include  
int select(nfds, readfds, writefds, exceptfds, timeout) 
int nfds; 
fd_set *readfds, *writefds, *exceptfds; 
struct timeval *timeout;

select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一Socket或文件可读写。当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用系统提供的宏FD_ISSET(int fd, fd_set *fdset) 在select()结束时便可判断哪一文件可读或可写。这样我们编写的socket只需要在有东西可读入或写的时候才进行操作。下面给出socket编程中select函数的通用范例:

int  sockfd, nRet; 
fd_set  fdR; 
struct  timeval timeout = ..; 

for(;;) { 
        FD_ZERO(&fdR); 
        FD_SET(sockfd, &fdR); 
        switch ((nRet =select(sockfd + 1, &fdR, NULL, &timeout))) { 
                case -1: 
                        error handled by u; 
                case 0: 
                        timeout hanled by u; 
                default:
                       printf(%d”,nRet);  //返回发生变化的句柄的个数
			if (FD_ISSET(sockfd)) {  //先判断一下socket这外被监视//的句柄是否真的变成可读的了
                                now u read or recv something; 
                                /* if sockfd is father and  
                                server socket, u can now 
                                accept() */ 
                        } 
        } 
}

FD_ISSET(sockfd)就相当通知了sockfd可读。

下面是select的具体实现。主要是参考了faraway的关于select的例子,他的例子实现得真的非常的好,而且下面的评论也给出了很多改进的方法。把这个例子弄懂,那么你对select应该就掌握得差不多了。同时我还把socket通信的两个函数封装了一下,为了以后再写socket通信不再做同类重复的工作。一个是服务器端的建立socket的ServerBuildSocket()函数,还有一个是客户端的ConnectServer()函数。当然我这里的实现都是按照具体一个工程的实现的角度来写的,不再是像以前那样都集成在一个简单的main函数里面。
config.h

#ifndef _CONFIG_H
#define _CONFIG_H

//用于控制输出的

#define _DEBUG
#ifdef _DEBUG
#define TRACE(...) printf(__VA_ARGS__)
#else
#define TRACE(...)
#endif   

#endif

select_server.c

#include "socketlib.h"
#include "config.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MYPORT 4444
#define BACKLOG 5
#define BUF_SIZE 200

int fd_A[BACKLOG];
int conn_amount;

int main()
{
    int nSocktfd, nNewSocktfd;
    int i,nRet,nMaxSock,nSockaddrLen;
    struct sockaddr_in client_addr;
    char szBuf[BUF_SIZE];
    fd_set fdRead;
    struct timeval tv;
    
    nSocktfd = ServerBuildSocket(MYPORT);
    if (nSocktfd < 0)
    {
        TRACE("ServerBuildSocket faile.\n");
        exit(0);
    }

    conn_amount = 0;
    nSockaddrLen = sizeof(client_addr);
    nMaxSock = nSocktfd; 

    while(1)
    {
        FD_ZERO(&fdRead);
        FD_SET(nSocktfd,&fdRead);

        tv.tv_sec = 30;
        tv.tv_usec = 0;

        //add active connect to fd set
        for (i = 0; i < BACKLOG; i++) {
            if (fd_A[i] != 0)
            {
                FD_SET(fd_A[i],&fdRead);
            }
        }

        nRet = select(nMaxSock + 1, &fdRead, NULL, NULL, &tv);
        if (nRet < 0)
        {
            TRACE("select failed.\n");
            break;
        }
        else if(nRet == 0)
        {
            TRACE("timeout.\n");
            continue;
        }

        //check whether the connection have data to recieve 
        for (i = 0; i < BACKLOG; i++)
        {
            if (fd_A[i] && FD_ISSET(fd_A[i],&fdRead))
            {
                nRet = recv(fd_A[i],szBuf,sizeof(szBuf),0);
                if (nRet <= 0)
                {
                    TRACE("Client %d close\n",i);
                    close(fd_A[i]);
                    FD_CLR(fd_A[i],&fdRead);
                    fd_A[i] = 0;
                    conn_amount --;
                }
                else
                {
                    if (nRet < BUF_SIZE)
                        memset(&szBuf[nRet],'\0',1);
                        TRACE("Client[%d] send: %s.\n",i,szBuf);
                }
            }
         }//for

        //check whether a new connection comes
        if (FD_ISSET(nSocktfd,&fdRead))
        {
            nNewSocktfd = accept(nSocktfd, (struct sockaddr *)&client_addr,&nSockaddrLen);
            if (nNewSocktfd <= 0)
            {
                TRACE("accept failed.\n");
                continue;
            }
            
            if (conn_amount < BACKLOG -1)
            {
                for(i = 0; i < BACKLOG; i++)
                {
                    if(fd_A[i] == 0)
                    {
                        fd_A[i] = nNewSocktfd;
                        conn_amount ++;
                        break;
                    }
                }
                TRACE("new connection client[%d] %s:%d\n", conn_amount,
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

                if (nNewSocktfd > nMaxSock)
                {
                    nMaxSock = nNewSocktfd;
                }
            }
            else
            {
                TRACE("max connections arrive,exit\n");
                send(nNewSocktfd,"bye",4,0);
                close(nNewSocktfd);
                continue;
            }
        }
       }//while

    //close all connections
    for (i = 0; i < BACKLOG; i++)
    {
        if (fd_A[i] != 0)
        {
            close(fd_A[i]);
        }
    }
    exit(0);
}

select_client.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "config.h"
#include "socketlib.h"

#define MYPORT 4444

int main()
{
    int nSockFd;
    int nLen;
    char szBuf[1024];

    nSockFd = ConnectServer("127.0.0.1",MYPORT);
    if (nSockFd < 0)
    {
        TRACE("ConnectServer failed.\n");
        exit(0);
    }

    while(fgets(nSockFd, 1024, stdin) != NULL)
    {
        if(send(nSockFd,szBuf,strlen(szBuf),0) <= 0)
        {
            TRACE("send failed.\n");
            exit(0);
        }
    }
    close(nSockFd);
    exit(0);
}

封装的函数:socketlib.h socketlib.c

#ifndef _SOCKETLIB_H
#define _SOCKETLIB_H

int ServerBuildSocket(unsigned int nPort);
int ConnectServer(const char *szIp,int nPort);

#endif
#include "socketlib.h"
#include 
#include 
#include 
#include 
#include 

int ServerBuildSocket(unsigned int port)
{
    //Build socket for server ,bind and listen the port
    
    int sock_fd,BACKLOG = 5;
    int nOptVal = 1;
    struct sockaddr_in server_addr;

    if ((sock_fd = socket(AF_INET,SOCK_STREAM,0)) == -1)
    {
        return -1; 
    }

    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,&nOptVal,sizeof(int)) == -1)
    {
        return -2;
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
    {
        return -3;
    }

    if (listen(sock_fd,BACKLOG) == -1)
    {
        return -4;
    }

    return sock_fd;

}

int ConnectServer(const char *szIp, int nPort)
{
    int nSockFd;
    int nAddrLen;
    struct sockaddr_in addr;

    if ((nSockFd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_port = htons(nPort);
    if (inet_aton(szIp, &addr.sin_addr) < 0)
    {
        return -2;
    }

    nAddrLen = sizeof(addr);
    if (connect(nSockFd,(struct sockaddr *)&addr, nAddrLen) == -1)
    {
        return -3;
    }
    return nSockFd;
}

server的makefile:

server:select_server.o socketlib.o 
    gcc -o server select_server.o socketlib.o
client:select_client.o socketlib.o
    gcc -o client select_client.o socketlib.o
select_server.o:select_server.c socketlib.h
    gcc -c select_server.c socketlib.h
select_client.o:select_client.c socketlib.h
    gcc -c select_client.c socketlib.h
socketlib.o:socketlib.c socketlib.h
    gcc -c socketlib.c socketlib.h

clean:
    rm -f select_sever.o socketlib.o

你可能感兴趣的:(Linux,linux,学习)