TCP套接字复用发送大数据

TCP套接字多路复用遇到了一些麻烦。

      //socket is non-blocking
      const int MAX = 4096;
      char *buff[MAX];
      char *p = buff;
      int fd, rvalue;
      rvalue = 0;

      if ( (fd = open(path, O_RDONLY)) < 0 ) {
          return errno;
      } else {
        int didsend, didread;
        int shouldsend;
        while ((didread = read(fd, buff, MAX)) > 0) {
          p = buff;
          shouldsend = didread;
          while ( 1 ) {
            didsend = send(sockfd, p, shouldsend, 0);
            //if send succeeds and returns the number of bytes fewer than asked for then try to send rest part in next time.
            if (didsend < shouldsend) {
              p += didsent;
              shouldsend -= didsend;
              continue;
            }
            //if there is no place for new data to send, then wait a brief time and try again.
            if ( didsend < 0 && (errno == EWOULDBLOCK || errno == EAGAIN) ) {
              usleep(1000);
              continue;
            }
            //if all data has been sent then sending loop is over.
            if (didsend == shouldsend) {
              break;
            }
            //send error
            if ( didsend < 0 ) {
              rvalue = errno;
              break;
            }
          }
        }
        close(fd);
        if (didread == -1) {
          return errno;
        }
        return rvalue;
      }

假设我使用I / O复用功能poll()或kqueue()和无阻塞套接字,那么如果只有一些小数据(如发送短消息),则可以正常工作。

但是如果涉及到大数据,我的意思是大于send()的缓冲区大小,因为使用非阻塞套接字,send()只会发送一部分数据,并返回发送的数据量,其余部分只能在另一个send()调用中发送数据,但是这需要时间,并且无法确定将花费多长时间。因此,第二个while()实际上是使用非阻塞套接字的阻塞发送。

相当于:

  //socket is blocking
  const int MAX = 4096;
  char *buff[MAX];
  int fd, n;
  if ( (fd = open(path, O_RDONLY)) < 0 ) {
      return errno;
  } else {
    while ((n = read(fd, buff, MAX)) > 0) {
      if (send(sockfd, buff, n, 0) < 0) {
        return errno;
      }
    }
    close(fd);
    return 0;
  }

因此,解决此问题的方法是什么,多线程可能会起作用,但这可能会浪费资源。

塞尔比

这是与多个连接和无阻塞套接字一起使用的单线程服务器的常规模式。

它主要是C语言中的伪代码,并且不执行必要的错误检查。但这给您一个想法,对于每个接受的连接,您都保留一个结构实例,该实例维护该套接字的套接字句柄,请求解析状态,响应流以及任何其他“状态”成员。然后,您可以使用“选择”循环来等待或让多个线程执行同一操作。

同样,这只是伪代码,以选择/轮询为例。您可以使用epoll获得更大的可伸缩性。

while (1)
{
    fd_set readset = {};
    fd_set writeset = {};

    for (int i = 0; i < number_of_client_connections; i++)
    {
        if (client_connections[i].reading_request)
            FD_SET(client_connection.sock, &readset);
        else
            FD_SET(client_connection.sock, &writeset);
    }

    // add the listen socket to the read set
    FD_SET(listen_socket, &readset);

    select(n + 1, &readset, &writeset, &timeout); // wait for a socket to be ready (not shown - check for errors and return value)

    if (FD_ISSET(listen_socket, &readset))
    {
        int new_client_socket = accept(listen_socket, &addr, &addrlength);

        // create a struct that keeps track of the connection state data
        struct ConnectionData client_connection = {};
        client_connection.sock = new_client_socket;
        client_connection.reading_request = 1;  // awaiting for all the request bytes to come in
        client_connections[number_of_client_connections++] = client_connection;  // pseudo code, add the client_connection to the list
    }


    for (int i = 0; i < number_of_client_connections; i++)
    {
        if (client_connections[i].reading_request)
        {
            if (FD_ISSET(client_connections[i], &readset))
            {
                char buffer[2000];
                int len = recv(client_connections[i].sock, buffer, 2000, 0);
                // not shown - handle error case when (recv < 0)
                // not shown - handle case when (recv == 0)
                ProcessIncomingData(client_connections[i], buffer, len);  // do all the request parsing here.  Flip the client_connections[i].reading_request to 0 if ready to respond
            }
        }
        else if (client_connections[i].reading_request == 0)
        {
            if (FD_ISSET(client_connections[i], &writeset))
            {
                client_connection* conn = &client_connections[i];
                int len = send(conn->sock, conn->response_buffer + conn->txCount, conn->response_size - conn->txCount, 0);
                conn->txCount += len;

                if (conn->txCount == conn->response_size)
                {
                    // done sending response - we can close this connection or change it to back to the reading state
                }
            }
        }
    }

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Java Tcp套接字发送额外的数据?

来自分类Dev

与Berkeley套接字复用

来自分类Dev

与Berkeley套接字复用

来自分类Dev

套接字无法发送数据

来自分类Dev

Java套接字发送数据

来自分类Dev

套接字无法发送数据

来自分类Dev

Java套接字发送数据

来自分类Dev

通过套接字传输大数据

来自分类Dev

通过TCP套接字发送多个文件

来自分类Dev

TCP套接字发送/接收始终等待

来自分类Dev

TCP:套接字发送/接收顺序

来自分类Dev

TCP套接字是否以发送数据的相同方式接收数据?

来自分类Dev

TCP套接字是否以发送数据的相同方式接收数据?

来自分类Dev

发送数据后,JMeter TCP Sampler不会关闭套接字

来自分类Dev

非阻塞tcp套接字如何在无法发送的数据包上通知应用程序。

来自分类Dev

通过C#中的TCP套接字方法发送位类型数据

来自分类Dev

如何使用C#套接字TCP连接调度发送/接收数据

来自分类Dev

发送数据后,JMeter TCP Sampler不会关闭套接字

来自分类Dev

是否必须连接 Python 3 TCP 套接字才能发送数据?或者不喜欢UDP?

来自分类Dev

Matlab TCP/IP 服务器套接字未发送准确数据

来自分类Dev

PHP反复从TCP套接字接收数据

来自分类Dev

使用从TCP套接字[python]接收的数据

来自分类Dev

C ++,TCP套接字无法接收数据

来自分类Dev

Android TCP 套接字不接收数据

来自分类Dev

套接字sendall()不发送数据

来自分类Dev

从结构发送数据,套接字编程

来自分类Dev

套接字-UDP通过Internet发送数据

来自分类Dev

通过套接字发送数据异常中止

来自分类Dev

套接字中的数据发送错误