I/O复用--Select

Select

    I/O复用,可以调用系统调用select和poll!在这两个系统调用中的某一个阻塞,而不是真正的阻塞I/O系统调用!

    select() 函数的重点在于它可以同时监控多个描述符(一般最大为1024),并且在描述符集中没有可操作的描述符时会进入睡眠状态。 实际应用中,若需要同时处理多个描述符的读写时,如果只是创建了一系列的read()和write()就会导致在有些描述符没有准备好读写时而被阻塞,这样当然不是我们期望的,因此这时就需要应用select()。

    下面主要介绍I/O复用中的select函数!select函数可以指示内核等待多个事件中的任一个发生,仅在一个或多个事件发生,或者等待一个足够的时间后才唤醒进程!

select函数的原型

select函数的原型如下:

#include <sys/types.h>

#include<sys/time.h>

int select ( int maxfdp1,

            fd_set  *readset ,

            fd_set  * writeset ,

            fd_set  *excpetset ,

            const struct timeval  *timeout);

参数

            readfds, writefds, exceptfds为所要监听的三个描述符集:

                ——readfds 监听文件描述符是否可读,不监听可以传入 NULL

                ——writefds 监听文件描述符是否可写 ,不监听可以传入 NULL

                ——exceptfds 监听文件描述符是否有异常,不监听可以传入 NULL

            maxfdp1 是 select() 监听的三个描述符集中描述符的最大值+1

            timeout 设置超时时间 ,它表示等待内核中的一组描述符任一个准备好需要花费多久的时间!其中timeval指定了秒数和微秒数。

            struct timeval  {

                long tv_sec;//秒数

                long tv_usec;//微秒数

            };

            select的超时时间,这个参数至关重要,它可以使select处于三种状态:

                第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;

                第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;

                第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

返回值

select有三个可能的返回值:

    1.正常情况下返回就绪的文件描述符个数;

    2.经过了timeout时长后仍无设备准备好,返回值为0;

    3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。

    4.如果出错,返回-1并设置相应的errno:

        EBADF 文件描述词为无效的或该文件已关闭

        EINTR 此调用被信号所中断

        EINVAL 参数n 为负值。

        ENOMEM 核心内存不足

描述字集

中间的三个参数readset writeset和excpetset指定我们要让内核测试读、写、异常条件所需的描述字!函数select使用描述字集,它一般是一个整型的数组,每个数中的每一位代表一个描述符。

系统提供了4个宏对描述符集进行操作:

#include <sys/select.h>

#include <sys/time.h>


//设置文件描述符集fdset中对应于文件描述符fd的位

void FD_SET(int fd, fd_set *fdset);


//清除文件描述符集fdset中对应于文件描述符fd的位(设置为0)

void FD_CLR(int fd, fd_set *fdset);


//清除文件描述符集fdset中的所有位(既把所有位都设置为0)

void FD_ZERO(fd_set *fdset);


//在调用select后使用FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置。

void FD_ISSET(int fd, fd_set *fdset);


例如下面一段代码:

fd_set readset;

FD_ZERO(&readset);

FD_SET(5, &readset);

FD_SET(33, &readset);

则文件描述符集readset中对应于文件描述符6和33的相应位被置为1

再执行如下程序后:

FD_CLR(5, &readset);

则文件描述符集readset对应于文件描述符6的相应位被置为0


通常,操作系统通过宏FD_SETSIZE来声明在一个进程中select所能操作的文件描述符的最大数目。

一般情况下被定义为1024.一个整数占4个 字节,既32位,那么就是用包含32个元素的整数数组来表示文件描述符集。

我们可以在头文件中修改这个值来改变select使用的文件描述符集的大小,但 是需要注意的是:必须重新编译内核才能使修改后的值有效。

如果我们对其中的某一个描述符集不感兴趣的话,可以设置为空指针。如果我们把三个描述符集都设为空指针,就实现了一个比sleep更准确的定时器。

注意select函数的第一个参数maxfdp1,是所有加入集合的句柄值的最大那个值还要加1。比如我们的描述符为1 4 5,那么maxfdp1就为6,描述符从0开始。

当我们调用函数时,指定我们关心的描述符集,当返回时,指示那些描述符已经准备好了。


注意事项 :

1. maxfdp1必须被正确设置,一般取描述符集中描述符的最大值并加1。

2. 在非必须的情况下,尽量使用不超时的select(),即将utimeout参数设置为NULL。

    /*参数 timeout 置为 NULL*/

    select(maxfdp1, &readfds, &writefds, &exceptfds, NULL);

3. timeout的值必须在每次select()之前重新赋值,因为操作系统会修改此值

    while(1)

    {

        timeout.tv_sec = 1;

        timeout.tv_usec = 0;

        select(maxfdp1, &readfds, &writefds, &exceptfds, &timeout);

    }

4. 由于select()会修改字符集,因此如果select()调用是在一个循环中,则描述符集必须被重新赋值。

    /*以read操作为例*/

    while(1)

        {

        FD_ZERO(&readfds);

        FD_SET(fd, &readfds);

        select(maxfdp1, &readfds, NULL, NULL, NULL);

    }

5. 函数read(),write(),recv(),send()以及select()可能会返回-1并且errno置位为EINTR,或这errno被赋值为EAGAIN(EWOULDBLOCK),这种情况需要被正确处理。如果程序中不接收任何信号,则不会得到EINTR。如果程序设为阻塞I/O,则不会收到EAGAIN。

     /*一般只需对EINTR进行处理就可以了,例子如下*/

    while(1)

    {

        ret = select(maxfdp1, &readfds, NULL, NULL, NULL);

        if(ret == -1 && errno == EINTR)

        continue;

    }

6. 当read(),write(),recv()和send()返回0时建议关闭描述符并在字符集中移除此描述符(不关闭描述符并移除的话可能会导致未知错误,还是对此情况处理的好)。

定时器 :

在没有usleep函数的系统中,可以应用select来实现,下例中实现了0.2秒的延时:

    struct timeval tv;

    tv.tv_sec = 0;

    tv.tv_usec = 200000; /* 0.2 秒*/

    select(0, NULL, NULL, NULL, &tv);

-------------------------------------------------------------------------------------------------------------------------

例子1:

-------------------------------------------------------------------------------------------------------------------------

main()

{

int sock;

FILE *fp;

struct fd_set fds;

struct timeval timeout={3,0}; //select等待3秒,3秒轮询,要非阻塞就置0

char buffer[256]={0}; //256字节的接收缓冲区

/* 假定已经建立UDP连接,具体过程不写,简单,当然TCP也同理,主机ip和port都已经给定,要写的文件已经打开

sock=socket(...);

bind(...);

fp=fopen(...); */

while(1)

{

    FD_ZERO(&fds); //每次循环都要清空集合,否则不能检测描述符变化

    FD_SET(sock,&fds); //添加描述符

    FD_SET(fp,&fds); //同上

    maxfdp=sock>fp?sock+1:fp+1; //描述符最大值加1

    switch(select(maxfdp,&fds,&fds,NULL,&timeout)) //select使用

    {

        //select错误,退出程序

        case -1:

            exit(-1);

            break; 

        //再次轮询

        case 0:

            break;

        default:

            //测试sock是否可读,即是否网络上有数据

            if(FD_ISSET(sock,&fds))

            {

                recvfrom(sock,buffer,256,.....);//接受网络数据

                //测试文件是否可写

                if(FD_ISSET(fp,&fds))

                {

                        fwrite(fp,buffer...);//写入文件

                }

                //buffer清空;

            }

            }// end if break;

        }// end switch

    }//end while

}//end main

-------------------------------------------------------------------------------------------------------------------------

例子2: Linux下用select查询串口数据

-------------------------------------------------------------------------------------------------------------------------

Linux下直接用read读串口可能会造成堵塞,或数据读出错误。然而用select先查询com口,再用read去读就可以避免,并且当com口延时时,程序可以退出,这样就不至于由于com口堵塞,程序就死了。我的代码如下:

bool ReadDevice( int hComm, unsigned long uLen, char* pData )

int nread = 0;

char inbuf[uLen];

char buff[uLen];

memset( inbuff, '\0', uLen );

memset( buff, '\0', uLen );

fd_set readset;

struct timeval tv;

int MaxFd = 0;

int c = 0;

int z;

do

{

FD_ZERO( &readset );

if( hComm >= 0 )

FD_SET( hComm, &readset );

MaxFd = hComm + 1;

tv.tv_sec = 0;

tv.tv_usec = 500000;

do

{

z = select( MaxFd, &readset, 0, 0, &tv);

}  while ( z==-1 && errno==EINTR );

if ( z == -1 )

printf("select(2)\n");

if( z == 0 )

{

hComm = -1;

}

if ( hComm>=0 && FD_ISSET(hComm, &readset) )

{

z = read( hComm, buff, uLen - c );

c += z;

if( z == -1 )

{

hComm = -1;

}

if ( z > 0 )

{

buff[ z + 1 ] = '\0';

strcat( inbuff, buff );

memset( buff, 0x00, uLen );

}

else

{

hComm = -1;

}

}

} while ( hComm >= 0 );

memcpy( pData, inbuff, c );

return true;

}

-------------------------------------------------------------------------------------------------------------------------

例子: 监视标准输入的变化,即文件描述符为0的变化,

-------------------------------------------------------------------------------------------------------------------------

#include <stdio.h>

#include <unistd.h>

#include <sys/time.h>

#include <sys/select.h>

#define BUF_SIZE 30

int main(int argc, char* argv[])

{

    fd_set reads, temps;

    int result, str_len;

    char buf[BUF_SIZE];

    struct timeval timeout;

    FD_ZERO(&reads);

    FD_SET(0, &reads);//监视文件描述符0的变化, 即标准输入的变化

    /*

    超时不能在此设置!

    因为调用select后,结构体timeval的成员tv_sec和tv_usec的值将被替换为超时前剩余时间.

    调用select函数前,每次都需要初始化timeval结构体变量.

    timeout.tv_sec = 5;

    timeout.tv_usec = 5000;

    */

    while(1)

    {

        /*

将准备好的fd_set变量reads的内容复制到temps变量,因为调用select函数后,除了发生变化的fd对应位外,剩下的所有位

都将初始化为0,为了记住初始值,必须经过这种复制过程。

        */

        temps = reads;

        //设置超时

        timeout.tv_sec = 5;

        timeout.tv_usec = 0;

        //调用select函数. 若有控制台输入数据,则返回大于0的整数,如果没有输入数据而引发超时,返回0.

        result = select(1, &temps, 0, 0, &timeout);

        if(result == -1)

        {

            perror("select() error");

            break;

        }

        else if(result == 0)

        {

            puts("timeout");

        }

        else

        {

            //读取数据并输出

            if(FD_ISSET(0, &temps))

            {

                str_len = read(0, buf, BUF_SIZE);

                buf[str_len] = 0;

                printf("message from console: %s", buf);

            }

        }

    }

    return 0;

}

程序运行结果:

/*

nihao

message from console: nihao

goodbye

message from console: goodbye

timeout

timeout

*/

-------------------------------------------------------------------------------------------------------------------------

例子:select函数实现I/O复用服务端

-------------------------------------------------------------------------------------------------------------------------

/*******************************************************

***服务器端***

********************************************************/

//server.c

#include <stdio.h>

#include <string.h>

#include <unistd.h>

#include <stdlib.h>

#include <arpa/inet.h>

#include <sys/time.h>

#include <sys/socket.h>

#include <sys/select.h>

#define BUF_SIZE 100

void error_handing(char* buf);

int main(int argc, char* argv[])

{

    int serv_sock, clnt_sock;

    struct sockaddr_in serv_adr, clnt_adr;

    struct timeval timeout;

    fd_set reads, cpy_reads;

    socklen_t adr_sz;

    int fd_max, str_len, fd_num, i;

    char buf[BUF_SIZE];

    if(argc != 2)

    {

        printf("Usage: %s <port> \n", argv[0]);

        exit(1);

    }

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);

    memset(&serv_adr, 0, sizeof(serv_adr));

    serv_adr.sin_family = AF_INET;

    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);

    serv_adr.sin_port = htons(atoi(argv[1]));

    if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)

        error_handing("bind() error");

    if(listen(serv_sock, 5) == -1)

        error_handing("listen() error");

    FD_ZERO(&reads);

    FD_SET(serv_sock, &reads); //将服务端套接字注册入fd_set,即添加了服务器端套接字为监视对象

    fd_max = serv_sock;

    while(1)

    {

        cpy_reads = reads;

        timeout.tv_sec = 5;

        timeout.tv_usec = 5000;

        //无限循环调用select 监视可读事件

        if((fd_num = select(fd_max+1, &cpy_reads, 0, 0, &timeout)) == -1)

        {

            perror("select error");break;

        }

        if(fd_num == 0)

            continue;

        for(i = 0; i < fd_max + 1; i++)

        {

            if(FD_ISSET(i, &cpy_reads))

            {

                /*发生状态变化时,首先验证服务器端套接字中是否有变化.

                ①若是服务端套接字变化,接受连接请求。

                ②若是新客户端连接,注册与客户端连接的套接字文件描述符.

                */

                if(i == serv_sock)

                {

                    adr_sz = sizeof(clnt_adr);

                    clnt_sock = accept(serv_sock, (struct sockaddr*)& clnt_adr, &adr_sz);

                    FD_SET(clnt_sock, &reads);

                    if(fd_max < clnt_sock)

                        fd_max = clnt_sock;

                    printf("connected client: %d \n", clnt_sock);

                }

                else   

                {

                    str_len = read(i, buf, BUF_SIZE);

                    if(str_len == 0)    //读取数据完毕关闭套接字

                    {

                        FD_CLR(i, &reads);//从reads中删除相关信息

                        close(i);

                        printf("closed client: %d \n", i);

                    }

                    else

                    {

                        write(i, buf, str_len);//执行回声服务  即echo

                    }

                }

            }

        }

    }

    close(serv_sock);

    return 0;

}

void error_handing(char* buf)

{

    fputs(buf, stderr);

    fputc('\n', stderr);

    exit(1);

}

/*******************************************************

***客户端***

********************************************************/

//client.c

#include <stdlib.h>

#include <stdio.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <string.h>

#define BUF_SIZE 1024

void error_handling(char* message);

int main(int argc, char* argv[])

{

    int sock;

    char message[BUF_SIZE];

    int str_len;

    struct sockaddr_in serv_adr;

    if(argc != 3)

    {

        printf("Usage: %s <IP> <port> \n", argv[0]);

        exit(1);

    }

    sock = socket(PF_INET, SOCK_STREAM, 0);

    if(sock==-1)

        error_handling("socket error");

    memset(&serv_adr, 0, sizeof(serv_adr));

    serv_adr.sin_family = AF_INET;

    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);

    serv_adr.sin_port = htons(atoi(argv[2]));

    if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)

        error_handling("connect() error!");

    else

        puts("connected....");

    while(1)

    {

        fputs("Input message:(输入Q退出):", stdout);

        fgets(message, BUF_SIZE, stdin);

        if(!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

            break;

        write(sock, message, strlen(message));

        str_len = read(sock, message, BUF_SIZE-1);

        message[str_len] = 0;

        printf("Message from server: %s", message);

    }

    close(sock);

    return 0;

}

void error_handling(char* message)

{

    fputs(message, stderr);

    fputc('\n', stderr);

    exit(1);

}

/*******************************************************

***编译执行测试***

********************************************************/

编译程序:

gcc server.c –o server

gcc client.c –o client

启动服务端:

yu@ubuntu:~/qtProjects/echo_selectserv$ ./server 8899

启动客户端1:

yu@ubuntu:~/qtProjects/echo_selectserv$ ./client 127.0.0.1 8899

connected....

Input message:(输入Q退出):你好哇2017年2月12日20:25:43

Message from server: 你好哇2017年2月12日20:25:43

Input message:(输入Q退出):你好哇2017-02-12 20:25:52

Message from server: 你好哇2017-02-12 20:25:52

Input message:(输入Q退出):q

启动客户端2:

yu@ubuntu:~/qtProjects/echo_selectserv$ ./client 127.0.0.1 8899

connected....

Input message:(输入Q退出):你好2017年2月12日20:25:11

Message from server: 你好2017年2月12日20:25:11

Input message:(输入Q退出):你好2017年2月12日20:25:24

Message from server: 你好2017年2月12日20:25:24

Input message:(输入Q退出):q

服务端情况:

yu@ubuntu:~/qtProjects/echo_selectserv$ ./server 8899

connected client: 4

connected client: 5

closed client: 5

closed client: 4

-------------------------------------------------------------------------------------------------------------------------

例子:

-------------------------------------------------------------------------------------------------------------------------

/*

使用select函数可以以非阻塞的方式和多个socket通信。程序只是演示select函数的使用,即使某个连接关闭以后也不会修改当前连接数,连接数达到最大值后会终止程序。

1. 程序使用了一个数组fd,通信开始后把需要通信的多个socket描述符都放入此数组

2. 首先生成一个叫sock_fd的socket描述符,用于监听端口。

3. 将sock_fd和数组fd中不为0的描述符放入select将检查的集合fdsr。

4. 处理fdsr中可以接收数据的连接。如果是sock_fd,表明有新连接加入,将新加入连接的socket描述符放置到fd。

*/

// select_server.c

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <errno.h>

#include <string.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/time.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#define MYPORT 1234 //连接时使用的端口

#define MAXCLINE 5 //连接队列中的个数

#define BUF_SIZE 200

int fd[MAXCLINE]; //连接的fd

int conn_amount; //当前的连接数

void showclient()

{

  int i;

  printf("client amount:%d\n",conn_amount);

  for(i=0;i<MAXCLINE;i++)

  {

    printf("[%d]:%d ",i,fd[i]);

  }

  printf("\n\n");

}

int main(void)

{

  int sock_fd,new_fd; //监听套接字 连接套接字

  struct sockaddr_in server_addr; // 服务器的地址信息

  struct sockaddr_in client_addr; //客户端的地址信息

  socklen_t sin_size;

  int yes = 1;

  char buf[BUF_SIZE];

  int ret;

  int i;

  //建立sock_fd套接字

  if((sock_fd = socket(AF_INET,SOCK_STREAM,0))==-1)

  {

    perror("setsockopt");

    exit(1);

  }

  //设置套接口的选项 SO_REUSEADDR 允许在同一个端口启动服务器的多个实例

  // setsockopt的第二个参数SOL SOCKET 指定系统中,解释选项的级别 普通套接字

  if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))==-1)

  {

    perror("setsockopt error \n");

    exit(1);

  }


  server_addr.sin_family = AF_INET; //主机字节序

  server_addr.sin_port = htons(MYPORT);

  server_addr.sin_addr.s_addr = INADDR_ANY;//通配IP

  memset(server_addr.sin_zero,'\0',sizeof(server_addr.sin_zero));

  if(bind(sock_fd,(struct sockaddr *)&server_addr,sizeof(server_addr)) == -1)

  {

    perror("bind error!\n");

    exit(1);

  }

  if(listen(sock_fd,MAXCLINE)==-1)

  {

    perror("listen error!\n");

    exit(1);

  }

  printf("listen port %d\n",MYPORT);

  fd_set fdsr; //文件描述符集的定义

  int maxsock;

  struct timeval tv;

  conn_amount =0;

  sin_size = sizeof(client_addr);

  maxsock = sock_fd;

  while(1)

  {

    //初始化文件描述符集合

    FD_ZERO(&fdsr); //清除描述符集

    FD_SET(sock_fd,&fdsr); //把sock_fd加入描述符集

    //超时的设定

    tv.tv_sec = 30;

    tv.tv_usec =0;

    //添加活动的连接

    for(i=0;i<MAXCLINE;i++)

    {

      if(fd[i]!=0)

      {

          FD_SET(fd[i],&fdsr);

      }

    }

    //如果文件描述符中有连接请求 会做相应的处理,实现I/O的复用 多用户的连接通讯

    ret = select(maxsock +1,&fdsr,NULL,NULL,&tv);

    if(ret <0) //没有找到有效的连接 失败

    {

      perror("select error!\n");

      break;

    }

    else if(ret ==0)// 指定的时间到,

    {

      printf("timeout \n");

      continue;

    }

    //循环判断有效的连接是否有数据到达

    for(i=0;i<conn_amount;i++)

    {

      if(FD_ISSET(fd[i],&fdsr))

      {

        ret = recv(fd[i],buf,sizeof(buf),0);

        if(ret <=0) //客户端连接关闭,清除文件描述符集中的相应的位

        {

          printf("client[%d] close\n",i);

          close(fd[i]);

          FD_CLR(fd[i],&fdsr);

          fd[i]=0;

          conn_amount--;


        }

        //否则有相应的数据发送过来 ,进行相应的处理

        else

        {

          if(ret <BUF_SIZE)

            memset(&buf[ret],'\0',1);

          printf("client[%d] send:%s\n",i,buf);

        }

      }

    }

    if(FD_ISSET(sock_fd,&fdsr))

      {

        new_fd = accept(sock_fd,(struct sockaddr *)&client_addr,&sin_size);

        if(new_fd <=0)

        {

          perror("accept error\n");

          continue;

        }

        //添加新的fd 到数组中 判断有效的连接数是否小于最大的连接数,如果小于的话,就把新的连接套接字加入集合

        if(conn_amount <MAXCLINE)

        {

          for(i=0;i< MAXCLINE;i++)

          {

            if(fd[i]==0)

            {

              fd[i] = new_fd;

              break;

            }

          }

        conn_amount++;

          printf("new connection client[%d]%s:%d\n",conn_amount,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));

          if(new_fd > maxsock)

          {

            maxsock = new_fd;

          }

        }


          else

          {

            printf("max connections arrive ,exit\n");

            send(new_fd,"bye",4,0);

            close(new_fd);

            continue;

          }

        }

        showclient();

      }

      for(i=0;i<MAXCLINE;i++)

      {

        if(fd[i]!=0)

        {

          close(fd[i]);

        }

      }


      exit(0);

  }


//客户端的一个简单的实现,只是为了证实一下,服务器端程序的正确性

//select_client.c

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

#include <sys/types.h>

#define MAXDATASIZE 100

#define SERVPORT 1234

#define MAXLINE 1024

int main(int argc,char *argv[])

{

  int sockfd,sendbytes;

  // char send[MAXLINE];

  char send[MAXLINE];

  char buf[MAXDATASIZE];

  struct hostent *host;

  struct sockaddr_in serv_addr;

  if(argc <2)

  {

    fprintf(stderr,"Please enter the server's hostname\n");

    exit(1);

  }

  if((host = gethostbyname(argv[1])) == NULL)

  {

    perror("gethostbyname");

    exit(1);

  }

  if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)

  {

    perror("socket error \n");

    exit(1);

  }

  serv_addr.sin_family = AF_INET;

  serv_addr.sin_port = htons(SERVPORT);

  serv_addr.sin_addr = *((struct in_addr *)host->h_addr);

  bzero(&(serv_addr.sin_zero),8);

  if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) ==-1)

  {

    perror("connect \n");

    exit(1);

  }

  while(fgets(send,1024,stdin)!=NULL)

  {

    if((sendbytes = write(sockfd,send,100)) ==-1)

  {

    perror("send error \n");

    exit(1);

  }

  }

  close(sockfd);

}

推荐阅读更多精彩内容

  • 原创:编程TWO编程小兔崽 今天跟大家介绍一个函数,这个函数在Linux编程里边特别重要,很多地方都用到。 sel...
    编程小兔崽阅读 199评论 0 0
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 427评论 0 5
  • 转载自I/O复用 select/poll/epoll 一、概述 I/O复用使得程序能同时监听多个文件描述符,这对提...
    吸霾少年阅读 117评论 0 2
  • 1、基本概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。IO多路复用适用...
    Daniel521阅读 193评论 0 0
  • 一、概述 I/O复用使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要。 I/O复用虽然能同时监听多个文...
    saviochen阅读 402评论 0 4