Linux I/O复用函数的使用情况和select接口的介绍

I/O 复用使得程序能同时监听多个文件描述符,这对于提高程序的性能至关重要。通常,
网络程序在下列情况下需要使用 I/O 复用技术:
1.TCP服务器同时要处理监听套接字和连接套接字

2.服务器同时要处理TCP请求和UDP请求。

3.程序同时要处理多个套接字。

4.客服端程序要同时处理用户输入和网络连接。

5.服务器要同时监听多个端口。

        需要指出的是, I/O 复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当
多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依处理其中的每一
个文件描述符,这使得服务器看起来好像是串行工作的。如果要提高并发处理的能力,可以
配合使用多线程或多进程等编程方法。

Linux I/O复用函数的使用情况和select接口的介绍_第1张图片

1select

1.1select的接口介绍

        select系统调用的用途是:再一段指定时间内,监听用户感兴趣的文件描述符的可读、可写和异常事件。

        select系统调用的原型如下:

# include 
int select (int maxfd,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,struct timeval*timeout);
/*
select 成功时返回就绪(可读、可写和异常)文件描述符的总数。如果在超时间内没有任何文件描述符就绪,select将返回0.select失败返回-1,如果是在select等待期间,程序接收到信号,则select立即返回-1,并设置errno为EINTR。
maxfd 参数指定的被监听的文件描述符的总数。它通常被设置为 select 监听的所
有文件描述符中的最大值+1
readfds、 writefds 和 exceptfds 参数分别指向可读、可写和异常等事件对应的文件
描述符集合。应用程序调用 select 函数时,通过这 3 个参数传入自己感兴趣的文件
描述符。 select 返回时,内核将修改它们来通知应用程序哪些文件描述符已经就绪
通过下列宏可以访问 fd_set 结构中的位:
FD_ZERO(fd_set *fdset); // 清除 fdset 的所有位
FD_SET(int fd, fd_set *fdset); // 设置 fdset 的位 fd,fd添加到集合(置一)
FD_CLR(int fd, fd_set *fdset); // 清除 fdset 的位 fd
int FD_ISSET(int fd, fd_set *fdset);// 测试 fdset 的位 fd 是否被设置
timeout 参数用来设置 select 函数的超时时间。它是一个 timeval 结构类型的指
针,采用指针参数是因为内核将修改它以告诉应用程序 select 等待了多久。 timeval
结构的定义如下
struct timeval
{
    long tv_sec;//秒数
    long tv_usec://微秒数
};
如果给 timeout 的两个成员都是 0,则 select 将立即返回。如果 timeout 传递
NULL,则 select 将一直阻塞,直到某个文件描述符就绪
*/

         Linux I/O复用函数的使用情况和select接口的介绍_第2张图片

 1.2select的示例代码

使用select实现的TCP服务器如下:

初始化服务器端的sockfd套接字

int initsocket()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if (sockfd==-1)
        return -1;
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof (saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(6000);
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof (saddr));
    if (res==-1)
        return -1;
    res=listen(sockfd,5);
    if (res==-1)
        return -1;
    return sockfd;
}

初始化记录服务器套接字的数组

void initFds(int fds[],int n)
{
    int i=0;
    for (;i

将套接字描述符添加到数组中

void AddFdToFds(int fds[],int fd,int n)
{   
    int i=0;
    for (;i

删除数组中的套接字描述符

void DelFromFds(int fds[],int fd,int n)
{
    for (int i=0;i

将数组中的套接字描述符设置到fd_set变量中,并返回当前最大文件描述符

int SetFsToFdset(fd_set*fdset,int fds[],int n)
{
    FD_ZERO(fdest);//fdest置零
    int i=0;
    int maxfd=fds[0];//最大文件描述符的值
    for (;imaxfd)
            {
                maxfd=fds[i];
            }
        }
    }
    return maxfd;
}

服务器和客户端的连接

void GetClientLink(int sockfd,int fds[],int n)
{    
    struct sockaddr_in caddr;
    int len=sizeof (caddr);
    memset(&caddr,0,sizeof(caddr));
    int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
    if (c<0)
    {
        return;
    }
    AddFdToFds(fds,c,n);
}    

处理客户端数据

void DealClientData(int fds[],int n,int clifd)
{
    char data[128];
    int num=recv(clifd,data,127,0);
    if (num<=0)
    {
        DelFdFromFds(fds,clifd,n);
        close(clifd);
    }
    else
    {
        printf ("%d:%s\n",clifd,data);
        send(clifd,"ok",2,0);
    }
}

处理select返回的就绪事件

void DealReadyEvent(int fds[],int n,fd_set*fdset,int sockfd)
{
    int i=0;
    for (;i

主函数

int main()
{
    int sockfd=initsocket();
    assert(sockfd!=-1);
    fd_set readfds;
    int fds[1024];
    initFds(fds,1024);
    AddFdToFds(fds,sockfd,1024);
    while(1)
    {
        int maxfd=SetFdToFdset(&readfds,fds,MAX_FD);
        struct timeval timeout;
        timeout.tv_sec=2;//秒数
        timeout.tv_usec=0;//微秒数
        int n=select(maxfd+1,&readfds,NULL,NULL,&timeout);
        if (n<0)
        {
            printf ("select error\n"); 
            break;   
        }
        else if (n==0)
        {
            printf ("timeout\n");
            continue;
        }
        DealReadyEvent(fds, 1024, &readfds, sockfd);
    }
    exit(0);
}

TCP 的客户端代码如下:
 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
int initsocket()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if (sockfd==-1)
        return -1;
    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof (saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(6000);
    saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof (saddr));
    if (res==-1)
        return -1;
    res=listen(sockfd,5);
    if (res==-1)
        return -1;
    return sockfd;
}
int main ()
{
    int sockfd=initsocket();
    assert(sockfd!=-1);
    while(1)
    {
        printf ("please input:");
        char buff[128]={0};
        fgets(buff,127,stdin);
        if (strncmp(buff,"bye",3))
        {
            break;
        }
        int n=send(sockfd,buff,strlen(buff)-1,0);
        if (n<=0)
        {
            printf ("send error\n");
            break;
        }
        memset(buff,0,128);
        n=recv(sockfd,buff,127,0);
        if(n<=0)
        {
            printf ("recv error\n");
            break;
        }
        printf ("%s\n",buff);
    }
    close(sockfd);
    exit(0);
}

你可能感兴趣的:(Linux学习笔记,linux,服务器,网络)