imjacob 发表于 2016-1-9 15:03:32

网络服务器 select同时处理读写的疑问

我现在实现个网络server有这样的需求,一般情况下是 从客户端读数据的,但是有时候需要往客户端主动去写数据,我想到了用select.

select的原型是 ( nfds, readfds,   writefds, errorfds, time)是这样的,看这个是能够同时处理 read和write的同时访问,
但我无论是 书上 和网络上,查了一下,select的一般例子都是 用于多客户端读的情况, 基本代码如下:


    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(9734);
    server_len = sizeof(server_address);

    bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

/*Create a connection queue and initialize readfds to handle input from server_sockfd.*/

    listen(server_sockfd, 5);

    FD_ZERO(&readfds);
    FD_SET(server_sockfd, &readfds);

/*Now wait for clients and requests.
    Since we have passed a null pointer as the timeout parameter, no timeout will occur.
    The program will exit and report an error if select returns a value of less than 1.*/

    while(1) {
      char ch;
      int fd;
      int nread;

      testfds = readfds;

      printf("server waiting\n");
      result = select(FD_SETSIZE, &testfds, (fd_set *)0,
            (fd_set *)0, (struct timeval *) 0);

      if(result < 1) {
            perror("server5");
            exit(1);
      }

/*Once we know we've got activity,
    we find which descriptor it's on by checking each in turn using FD_ISSET.*/

      for(fd = 0; fd < FD_SETSIZE; fd++) {
            if(FD_ISSET(fd,&testfds)) {

/*If the activity is on server_sockfd, it must be a request for a new connection
    and we add the associated client_sockfd to the descriptor set.*/

                if(fd == server_sockfd) {
                  client_len = sizeof(client_address);
                  client_sockfd = accept(server_sockfd,
                        (struct sockaddr *)&client_address, &client_len);
                  FD_SET(client_sockfd, &readfds);
                  printf("adding client on fd %d\n", client_sockfd);
                }

/*If it isn't the server, it must be client activity.
    If close is received, the client has gone away and we remove it from the descriptor set.
    Otherwise, we 'serve' the client as in the previous examples.*/

                else {
                  ioctl(fd, FIONREAD, &nread);

                  if(nread == 0) {
                        close(fd);
                        FD_CLR(fd, &readfds);
                        printf("removing client on fd %d\n", fd);
                  }

                  else {
                        read(fd, &ch, 1);
                        sleep(5);
                        printf("serving client on fd %d\n", fd);
                        ch++;
                        write(fd, &ch, 1);
                  }
                }
            }
      }
    }


那想请教如何用select实现 读写都可以呢?

tcm123 发表于 2016-1-9 17:32:52

因为读不知道什么时候有数据来,所以才用SELECT, 要写直接写就行了

imjacob 发表于 2016-1-9 18:29:28

tcm123 发表于 2016-1-9 17:32
因为读不知道什么时候有数据来,所以才用SELECT, 要写直接写就行了

在同一个线程里吗? 譬如我的例子,写在哪里

imjacob 发表于 2016-1-9 21:28:57

本帖最后由 imjacob 于 2016-1-9 22:10 编辑

又仔细查了些资料,加上自己的思考,是否可以这样,基本原理是 每次读写 读写之前用select判断是否可读或者可写,代码大致如下

    //这里区分是需要读还是写
BOOL NET::SelectFun(SOCKET sock,BOOL ret) //ret为true表示要读取数据,ret为false表示要发送数据
{
    if (ret) //ret只是用来区分读写
    {
       num=select(0,&fd,NULL,NULL,&tval);
    }
    else
    {
      num=select(0,NULL,&fd,NULL,&tval);
    }
}

//接收的线程里
UINT NET::ThreadReceive()
{
      while (1)
    {
      if(NET->SelectFun(NET->m_sock,TRUE)) // 判断,如果select判断为真则读取数据
      {

            res=recv ()

      }
}


//发送的线程里
UINT NET::ThreadSend()
{
      while (1)
    {
      if(NET->SelectFun(pDlg->m_sock,TRUE)) // 判断,如果select判断为真则读取数据
      {

            res=send()         

      }
}


虽然感觉可以,但是需要用两个线程,能不能用一个呢?两个的情况下,这种所谓的IO多路复用方式没体现出优势啊,因为我原始的用socket分别在两个socket读和写也是可以的啊。恳请各位指教。

当然在另外一个帖子里,有朋友说那些libuv之类的库,我看了下确实能够比较好的解决这个问题,但担心效率速度。
因为是在单片机里,那些库要占用额外的资源的,所以不太敢用。

imjacob 发表于 2016-1-10 15:52:53

更新一下,想到的另外一个方式是:
一个线程专门写, 不能阻塞
另一个线程用select来查询是否可读。
页: [1]
查看完整版本: 网络服务器 select同时处理读写的疑问