unix下网络编程之I/O复用(二)

select函数

该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它。我们调用select告知内核对哪些描述字(就读、写或异常条件)感兴趣以及等待多长时间。我们感兴趣的描述字不局限于套接口,任何描述字都可以使用select来测试。

select函数原型:

#include<sys/select.h>#include<sys/time.h>int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);返回:就绪描述字的正数目,0——超时,-1——出错

select函数的参数介绍:maxfd表示待测试的描述字个数,其值应该为最大的描述字+1,中间的readset,writeset,exceptionset指定我们要让内核测试读、写、异常条件的描述字,最后一个参数告知内核等待所指定描述字中的任何一个就绪可花多长时间。

timeval结构:

struct timeval {long tv_sec; //secondslong tv_usec ; //microseconds}

timeval参数有三种可能值:1、NULL:代表永远等待下去,相当于完全阻塞。2、一个固定的值,代表等待一段固定的时间。3、timeval的属性值为0,表示根本不等待,检查描述字之后立即返回,也就是说事非阻塞的。

fd_set结构:

fd_set结构表示一个描述字集。它典型的应该以一个整数数组来表示,其中每个整数中的每一位对应一个描述字。关于fd_set有以下四个宏:

void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */

select函数修改由指针readset,writeset,exceptionset所指向的描述字集,因而这三个参数都是值-结果参数。也就是说,在select函数执行过程中,会修改其中的值。调用该函数时,我们指定关心的描述字的值,该函数返回时,结果指示哪些描述字已就绪。该函数返回后,我们使用FD_ISSET来测试fd_set数据类型中的描述字。描述字集中任何与未就绪的描述字对应的位返回时均清为0.为此,每次重新调用select函数中,我们都得再次把所有描述字集合中的所关心的位置为1。这也是在稍候的通信例子里,我们设置resset和allset两个集合的原因所在。

select函数返回某个套接口就绪的条件:

select函数的通信例子:一个简单的TCP回射服务器程序

SelectServer.c: 使用select机制的服务器端程序

  1 #include <stdio.h>  2 #include <string.h>  3 #include <arpa/inet.h>  4 #include <netinet/in.h>  5 #include <sys/socket.h>  6 #include <sys/select.h>  7   8 const static int MAXLINE = 1024;  9 const static int SERV_PORT = 10001; 10  11 int main1() 12 { 13     int i , maxi , maxfd, listenfd , connfd , sockfd ; 14     /*nready 描述字的数量*/ 15     int nready ,client[FD_SETSIZE]; 16     int n ; 17     /*创建描述字集合,由于select函数会把未有事件发生的描述字清零,所以我们设置两个集合*/ 18     fd_set rset , allset; 19     char buf[MAXLINE]; 20     socklen_t clilen; 21     struct sockaddr_in cliaddr , servaddr; 22     /*创建socket*/ 23     listenfd = socket(AF_INET , SOCK_STREAM , 0); 24     /*定义sockaddr_in*/ 25     memset(&servaddr , 0 ,sizeof(servaddr)); 26     servaddr.sin_family = AF_INET; 27     servaddr.sin_port = htons(SERV_PORT); 28     servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 29  30     bind(listenfd, (struct sockaddr *) & servaddr , sizeof(servaddr)); 31     listen(listenfd , 100); 32     /*listenfd 是第一个描述字*/ 33     /*最大的描述字,用于select函数的第一个参数*/ 34     maxfd = listenfd; 35     /*client的数量,用于轮询*/ 36     maxi = -1; 37     /*init*/ 38     for(i=0 ;i<FD_SETSIZE ; i++) 39         client[i] = -1; 40     FD_ZERO(&allset); 41     FD_SET(listenfd, &allset); 42  43     for (;;) 44     { 45         rset = allset; 46         /*只select出用于读的描述字,阻塞无timeout*/ 47         nready = select(maxfd+1 , &rset , NULL , NULL , NULL); 48         if(FD_ISSET(listenfd,&rset)) 49         { 50             clilen = sizeof(cliaddr); 51             connfd = accept(listenfd , (struct sockaddr *) & cliaddr , &clilen); 52             /*寻找第一个能放置新的描述字的位置*/ 53             for (i=0;i<FD_SETSIZE;i++) 54             { 55                 if(client[i]<0) 56                 { 57                     client[i] = connfd; 58                     break; 59                 } 60             } 61             /*找不到,说明client已经满了*/ 62             if(i==FD_SETSIZE) 63             { 64                 printf("Too many clients , over stack .\n"); 65                 return -1; 66             } 67             FD_SET(connfd,&allset);//设置fd 68             /*更新相关参数*/ 69             if(connfd > maxfd) maxfd = connfd; 70             if(i>maxi) maxi = i; 71             if(nready<=1) continue; 72             else nready --; 73         } 74  75         for(i=0 ; i<=maxi ; i++) 76         { 77             if (client[i]<0) continue; 78             sockfd = client[i]; 79             if(FD_ISSET(sockfd,&rset)) 80             { 81                 n = read(sockfd , buf , MAXLINE); 82                 if (n==0) 83                 { 84                     /*当对方关闭的时候,server关闭描述字,并将set的sockfd清空*/ 85                     close(sockfd); 86                     FD_CLR(sockfd,&allset); 87                     client[i] = -1; 88                 } 89                 else 90                 { 91                     buf[n]=‘\0‘; 92                     printf("Socket %d said : %s\n",sockfd,buf); 93                     write(sockfd,buf,n); //Write back to client 94                 } 95                 nready --; 96                 if(nready<=0) break; 97             } 98         } 99 100     }101     return 0;102 }

Client.c: 简单的客户端程序

 1 #include <stdio.h> 2 #include <string.h> 3 #include <arpa/inet.h> 4 #include <netinet/in.h> 5 #include <sys/socket.h> 6  7 #define MAXLINE 1024 8 int main() 9 {10     int sockfd ,n;11     char buf [MAXLINE];12     sockfd = socket(AF_INET,SOCK_STREAM ,0);13     struct sockaddr_in servaddr;14     memset(&servaddr, 0 ,sizeof(servaddr));    15     servaddr.sin_family = AF_INET;16     servaddr.sin_port = htons(10001);17     inet_pton( AF_INET ,"127.0.0.1" , &servaddr.sin_addr ) ;18 19     connect(sockfd,(struct sockaddr *)&servaddr , sizeof(servaddr));20     while(1)21     {22         printf("type some words ...\n");23         scanf("%s",buf);24         write(sockfd,buf,sizeof(buf));25         n = read(sockfd,buf,MAXLINE);26         printf("%d bytes received \n ",n);27         buf[n] = ‘\0‘;28         printf("%s\n",buf);29     }30     close(sockfd);31     return 0;32 }

 

总结:

在本文中,介绍了select函数在I/O复用中相关内容,并给出了一个典型的TCP反射程序的样例,使用select可以处理多个客户端的并发需求。在下一篇文章中,将会重点介绍另一种常见的I/O复用函数poll的使用。

原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972151.html

时间: 2024-11-10 22:39:43

unix下网络编程之I/O复用(二)的相关文章

unix下网络编程之I/O复用(三)

poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息. poll函数原型: 1 2 3 #include<poll.h>    int poll (struct pollfd * fdarray , unsigned long nfds , int timeout);    //返回:就需描述字的个数,0--超时,-1--出错

unix下网络编程之I/O复用(一)

什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or more I/O conditions are ready (i.e., input is ready to be read, or the descriptor is capable of taking more output). This capability is called I/O multi

unix下网络编程之I/O复用(五)

前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客户端从标准输入读入一行,发送到服务端 服务端从网络读取一行,然后输出到客户端 客户端收到服务端的响应,输出这一行到标准输出 服务端 代码如下: #include <unistd.h>#include <sys/types.h> /* basic system data types */

Java网络编程之Socket通信(二)

之前在前面已经介绍了Socket通信的一些基本原理,以及如何让客户端与服务器端建立通信,和实现通信的一些基本步骤(包括首先使得服务器端与客户端建立连接,建立连接之后,服务器端开始侦听客户端的请求,侦听到客户端的请求之后,通过输入输出流处理相关信息实现通信,最后通信完毕结束通信等一系列流程). 但是之前只是单个客户端与服务器进行通信,而我们实际应用中单个客户端的情况几乎不存在,都是多个客户端同时与服务器进行交互(这里同时交互就会出现并发性的问题,对于并发性的问题暂时还不是很懂,只知道有这个概念),

Unix网络编程之IO复用

上篇存在的问题 在上一篇TCP套接字中,还存在着一些问题. 当客户端连接上服务器后,阻塞于从标准输入读入信息的状态,若此时服务器进程被杀死,即使给客户TCP发来一个 FIN结束分节,但是由于客户处于阻塞状态,它将看不到这个EOF,直到读取之后,此时可能已经过去了很长时间. 因此进程需要一种能力,让内核同时检测多个IO口是否就绪,这个能力就称为IO复用.这是由select和poll两个函数 支持的. select函数 作用 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或

网络编程之IO模型——selectors模块

网络编程之IO模型--selectors模块 一.了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了充分利用网络连接的物理介质. 往往在同一条网络链路上采用时分复用或频分复用的技术使其在同一链路上传输多路信号,到这里我们就基本上理解了复用的含义, 即公用某个"介质"来尽可能多的做同一类(性质)的事,那IO复用的"介质"是什

扯谈网络编程之Tcp SYN flood洪水攻击

简介 TCP协议要经过三次握手才能建立连接: (from wiki) 于是出现了对于握手过程进行的攻击.攻击者发送大量的FIN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries).这样的话,对于服务器的内存,带宽都有很大的消耗.攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难. 攻与防 攻击者角度 从攻击者的角度来看,有两个地方可以

【转】JAVA网络编程之Socket用法

JAVA网络编程之Socket用法 分类: JAVA2012-08-24 15:56 710人阅读 评论(0) 收藏 举报 在客户/服务器通信模式中,客户端需要主动建立与服务器连接的Socket,服务器端收到客户端的连接请求,也会创建与客户端连接的Socket.Socket可以看做是通信连接两端的收发器,客户端和服务店都通过Socket来收发数据. 1.构造Socket public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 public Socket(Str

[深入浅出WIndows 10]网络编程之HttpClient类

14.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类提供了一个简单的接口来处理最常见的任务,并为身份验证提供了适用于大多数方案的合理的默认设置.对于较为复杂的 HTTP 操作,更多的功能包括:执行常见操作(DELETE.GET.PUT 和 POST)的方法:获取.设置和删除 Cookie 的功能:支持常见的身份验证设置和模式:异步方法上提供的 HTTP