简体   繁体   English

有什么方法可以知道从客户端发送到服务器的字节数并在网络中处理 recv()

[英]Is there any way to know the amount of bytes send from the client to the server and process the recv() in networks

I am trying to build a chat application between the server and the client.我正在尝试在服务器和客户端之间构建一个聊天应用程序。 My doubt is for sending information from the client or from the server I was able to handle the partial send with the help of the loop, but I am unable to find out the length of the send data bytes from the client to the server or from the server to the client, thereby having problem in creating the memory for the received bytes and printing.我的疑问是从客户端或服务器发送信息我能够在循环的帮助下处理部分发送,但我无法找出从客户端到服务器或从服务器发送数据字节的长度服务器到客户端,因此在为接收的字节和打印创建 memory 时遇到问题。

My chat function code for the client:我的聊天 function 客户端代码:


int chat_function(int sockfd) 
{ 
    char ch;
    char *buf;
    char *newp;
    int ret_send = 0;
    int ret_recv = 0;
    int buf_size = 0; 

    while(1) { 
        printf("From client, enter the message : ");
        buf = (char *)malloc(sizeof(char));
        if (buf == NULL)
            return -1;  
        while ((ch = getchar()) != '\n') {
            buf[buf_size++] = ch;
            newp = (char *)realloc(buf, (buf_size + 1) * sizeof(char));
            if ( newp == NULL) {
                free(buf);
                return -1;
            }
            buf = newp; 
        }
        buf[buf_size] = '\0';
        ret_send = send_all(sockfd, buf, buf_size);
        if (ret_send == -1)
            error(1, errno, "error in send() function call\n");
        memset(buf, 0, buf_size);
        ret_recv = recv_all(sockfd, buf, buf_size);
        if (ret_recv == -1) {
            error(1, errno, "error in recv() function call\n");
        } else if (ret_recv == -2) {
            printf("Oops the server has closed the connection\n");
            free(buf);
            break;
        }
        printf("From Server : %s", buf); 
        if ((strncmp(buf, "exit", 4)) == 0) { 
            printf("Client Exit...\n");
            free(buf); 
            break; 
        }
        free(buf);
    } 
} 

For handling partial send:对于处理部分发送:

int send_all(int sockfd, char *buf, int buf_size)
{
    int bytes_left = 0;
    size_t send_bytes = 0;

    bytes_left = buf_size
    while (1) {
        send_bytes = send(fd, buf, bytes_left, 0);
        if (send_bytes == -1)
            return -1;
        buf = buf + send_bytes;
        bytes_left = bytes_left - send_bytes;
        if (bytes_left == 0)
            break;
    }
    return 0;
}

There is no such mechanism built into TCP or UDP. TCP 或 UDP 中没有内置这种机制。 You need to implement your own protocol on top of it.你需要在它之上实现你自己的协议。 One of the possible solutions is:一种可能的解决方案是:

  • If the content delivered is static.如果交付的内容是 static。

If the sending end knows the size of the data that is being delivered prior, your client and server can agree on specific terms.如果发送端知道之前发送的数据的大小,您的客户端和服务器可以就特定条款达成一致。 For example, the first four bytes sent by the server is the size of the remaining message represented in network byte order.例如,服务器发送的前四个字节是以网络字节顺序表示的剩余消息的大小。

Server code服务器代码

uint32_t n_size = htonl(size);  // Convert the data size into network byte order.

write(sockfd, &n_size, sizeof(n_size)); // Send to the client.

Client code客户端代码

uint32_t n_size;

int n_read = 0;

for ( ; ; ) {

    int rd_status = read(sockfd, (void*) &n_size + n_read, sizeof(n_size) - n_read);

    if (rd_status <= 0)
        goto handle_this_case;

    n_read = n_read + rd_status;

    if (n_read == sizeof(n_size))
        break;
}

uint32_t size = ntohl(n_size);
  • If the content delivered is generated on the fly.如果交付的内容是动态生成的。

In this case, even the server is not aware of the size of the message.在这种情况下,即使服务器也不知道消息的大小。 You need to build your functions for handling this case.您需要构建处理这种情况的函数。 Below I have shown a bare minimal implementation:下面我展示了一个简单的最小实现:

Client-Side:客户端:

struct data_unit
{
    void* data;
    int size;
};

struct data_storage
{
    struct data_unit unit;
    struct data_storage* next;
};

void append_data(struct data_storage* storage, struct data_unit* unit);

struct data_unit* dump_data(struct data_storage* storage);

int main()
{
    struct data_storage storage;

    struct data_unit unit;
    unit.data = malloc(MAX_SIZE);

    for ( ; ; ) {

        int rd_status = read(sockfd, unit.data, MAX_SIZE);

        if (rd_status < 0)
            goto handle_this_case;
        else if (rd_status == 0)
            break;

        unit.size = rd_status;

        append_data(&storage, &unit);
    }

    struct data_unit* t_data = dump_data(&storage);
}

TCP is a stream protocol, meaning there are no message boundaries: it is just a full-duplex (meaning data flows in both directions at the same time, as if there were two separate lanes) more or less continuous stream of data. TCP 是一个 stream 协议,意味着没有消息边界:它只是一个全双工(意味着数据同时在两个方向上流动,好像有两个单独的通道)或多或少连续的 ZF7B44CFAFD15C5222E7B54 数据

UDP is a datagram protocol, and does have message boundaries. UDP 是一种数据报协议,并且确实有消息边界。 There is an ioctl (FIONREAD/SIOCINQ) that provides the length of the next datagram, but because it involves a syscall, doing that for every message you receive is going to be slow and inefficient.有一个 ioctl (FIONREAD/SIOCINQ) 提供下一个数据报的长度,但是因为它涉及一个系统调用,所以对你收到的每条消息都这样做会很慢而且效率很低。 Instead, you normally use a buffer large enough to hold the largest acceptable message, and copy it if/when necessary.相反,您通常使用足够大的缓冲区来保存最大的可接受消息,并在必要时复制它。 However, UDP also has no reliability guarantees, and often UDP datagrams are completely lost without any trace or discernible reason;但是,UDP 也没有可靠性保证,并且经常 UDP 数据报完全丢失,没有任何痕迹或可辨别的原因; that's just what happens.这就是发生的事情。

For a chat client-server connection, you'll want to use TCP.对于聊天客户端-服务器连接,您需要使用 TCP。

Since the underlying connection is just a stream of data, you need to design a protocol for the communications, so that the stream can be split into messages , with each message processed separately.由于底层连接只是一个 stream 数据,因此需要设计一个通信协议,以便将 stream 拆分为消息,每个消息单独处理。

The simplest case would be to use the nul character, \0 , as a message separator.最简单的情况是使用 nul 字符\0作为消息分隔符。

The "send" function would then look something like this: “发送” function 看起来像这样:

/* Returns 0 if message successfully sent,
   nonzero errno code otherwise. */
int  send_message(int descriptor, const char *message)
{
    /* If message is NULL, we cannot use strlen(); use zero for that. */
    const size_t  message_len = (message) ? strlen(message) : 0;

    /* Temporary variables for the sending part. */
    const char        *ptr = message;
    const char *const  end = message + message_len + 1; /* Include '\0' at end */
    ssize_t            bytes;

    /* Check valid descriptor and message length. */
    if (descriptor == -1 || message_len < 1)
        return errno = EINVAL;

    /* Write loop for sending the entire message. */
    while (ptr < end) {
        bytes = write(descriptor, ptr, (size_t)(end - ptr));
        if (bytes > 0) {
            ptr += bytes;
        } else
        if (bytes != -1) {
            /* This should never happen. */
            return errno = EIO;
        } else
        if (errno != EINTR) {
            /* We do not consider EINTR an actual error; others we do. */
            return errno;
        }
    }

    return 0;
}

The above send_message() function writes the specified string, including the string terminating nul character \0 , to the specified descriptor.上面的send_message() function 将指定的字符串(包括以 nul 字符\0结尾的字符串)写入指定的描述符。

On the read end, we need a buffer large enough to hold at least one full message.在读取端,我们需要一个足够大的缓冲区来容纳至少一条完整的消息。 Instead of always waiting for incoming data, we need to check if the buffer already contains a full message, and if it does, return that.我们需要检查缓冲区是否已经包含完整的消息,而不是总是等待传入的数据,如果是,则返回该消息。 Also, you do not necessarily want to always wait for an incoming message, because that would mean you cannot send two messages in a row.此外,您不必总是等待传入消息,因为这意味着您不能连续发送两条消息。

So, here's my suggestion:所以,这是我的建议:

static int     incoming_desc = -1;
static char   *incoming_data = NULL;
static size_t  incoming_size = 0;
static char   *incoming_next = NULL;  /* First received but not handled */
static char   *incoming_ends = NULL;  /* Last received but not handled */

#define  INCOMING_CHUNK  4096

/* Receive a new message into dynamically allocated buffer,
   and return the length.  Returns 0 when no message, with errno set.
   Waits at most ms milliseconds for a new message to arrive.
   errno == EAGAIN: no message, timeout elapsed.
   errno == ECONNABORTED: other end closed the connection.
*/
size_t  get_message(char **message, size_t *size, long ms) 
{
   struct timeval  timeout;

   /* Make sure the parameters are sane. */
   if (!message || !size || ms < 0) {
       errno = EINVAL;
       return 0;
   }

   /* For this function to work like getline() and getdelim() do,
      we need to treat *message as NULL if *size == 0. */
   if (!*size)
       *message = NULL;

   timeout.tv_sec = ms / 1000;
   timeout.tv_usec = (ms % 1000) * 1000;

   /* Timeout loop. */
   while (1) {
       fd_set  readfds;
       ssize_t bytes;
       size_t  used;
       int     result;

       /* Is there a pending complete message in the buffer? */
       if (incoming_ends > incoming_next) {
           char *endmark = memchr(incoming_next, '\0', (size_t)(incoming_ends - incoming_next));
           if (endmark) {
               const size_t  len = (size_t)(endmark - incoming_next) + 1;

               /* Reallocate the message buffer, if necessary. */
               if (len > *size) {
                   char *temp = realloc(*message, len);
                   if (!temp) {
                       errno = ENOMEM;
                       return 0;
                   }
                   *message = temp;
                   *size = len;
               }

               /* Copy message, */
               memcpy(*message, incoming_next, len);

               /* and remove it from the buffer. */
               incoming_next += len;

               /* In case the other end sent just the separator, clear errno. */
               errno = 0;

               /* We return the length sans the separator. */
               return len - 1;
           }
       }

       /* Do we have time left to check for input? */
       if (timeout.tv_sec <= 0 && timeout.tv_usec <= 0)
           break;  /* Nope. */

       /* Is incoming_desc one we can select() for? */
       if (incoming_desc < 0 || incoming_desc >= FD_SETSIZE)
           break;  /* Nope. */

       FD_ZERO(&readfds);
       FD_SET(incoming_desc, &readfds);
       result = select(incoming_desc + 1, &readfds, NULL, NULL, &timeout);
       if (result < 1)
           break;  /* Nothing interesting happened (we ignore error here). */
       if (!FD_ISSET(incoming_fd, &readfds))
           break;

       /* Number of bytes used in the buffer right now. */
       used = (size_t)(incoming_ends - incoming_data);

       /* Do we have at least INCOMING_CHUNK bytes available? */
       if (used + INCOMING_CHUNK >= incoming_size) {
           /* Nope.  Repack the incoming buffer first. */
           if (incoming_next > incoming_data) {
               const size_t  len = (size_t)(incoming_ends - incoming_next);
               if (len > 0)
                   memmove(incoming_data, incoming_next, len);
               incoming_next = incoming_data;
               incoming_ends = incoming_data + len;
           }
           /* Recalculate the number of bytes we have free now. Enough? */
           used = (size_t)(incoming_ends - incoming_data);
           if (used + INCOMING_CHUNK > incoming_size) {
               /* Grow incoming buffer. */
               const size_t  newsize = used + INCOMING_CHUNK;
               char *temp = realloc(incoming_data, newsize);
               if (!temp) {
                   errno = ENOMEM;
                   return 0;
               }
               incoming_next = temp + (size_t)(incoming_next - incoming_data);
               incoming_ends = temp + used;
               incoming_data = temp;
               incoming_size = newsize;                   
           }
       }

       /* Read more data into the buffer; up to a full buffer. */
       bytes = read(incoming_fd, incoming_ends, incoming_size - used);
       if (bytes > 0) {
           incoming_ends += bytes;
       } else
       if (bytes == 0) {
           /* Other end closed the connection.  We may have a partial message
              in the buffer, and should handle that too, but for now, we
              just error out. */
           errno = ECONNABORTED;
           return 0;
       } else
       if (bytes != -1) {
           /* Should never happen. */
           errno = EIO;
           return 0;
       } else
       if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
           /* No data yet, interrupted by signal delivery, etc. */
           continue;
       } else {
           /* errno is set to indicate which error happened. */
           return 0;
       }
    }

    /* Timeout. */
    errno = EAGAIN;
    return 0;
}

Note that get_message() works like getline() : you do eg请注意get_message()的工作方式类似于getline() :您可以这样做

char   *msg = NULL;
size_t  size = 0;
size_t  len;

len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */
if (len) {
    /* msg contains a full message of len characters */
} else
if (errno == ECONNABORTED) {
    /* Other end closed the connection */
} else
if (errno != EAGAIN) {
    fprintf(stderr, "Error receiving data: %s.\n", strerror(errno));
}

Then, you can reuse the same dynamically allocated buffer by just calling eg然后,您可以通过调用例如重用相同的动态分配的缓冲区

len = get_message(&msg, &size, 100); /* 100 ms = 0.1 seconds */

again.再次。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C套接字客户端没有recv() - 来自服务器的任何字节 - C Socket Client not recv()-ing any bytes from server C 中的客户端和服务器 send() 和 recv() - Client and Server send() and recv() in C 确定使用recv读取的字节数 - Determining amount of bytes read with recv 更改客户端上的网络IP后,可以在C(服务器)中从Android(客户端)发送但无法接收 - Can send but not receive in C (server), from Android (client), after changing networks IP on client (C套接字编程)将服务器的send()调用与结束于同一客户端recv()缓冲区中的服务器分开 - (C Socket Programming) Seperate send() calls from server ending up in same client recv() buffer 为什么 kernel 在发送一定数量的字节后从客户端强行发送 TCP RST? - Why kernel forcefully sends TCP RST from client after certain amount of bytes send? Winsock:服务器上的 recv() 被阻塞,但客户端已经移过 send() - Winsock: recv() on Server is blocking, but client has already moved past send() C、recv_all、send_all 中的 TCP 回显服务器/客户端 - 由我实现,recv 不起作用 - TCP echo server / client in C, recv_all, send_all - implemented by me, recv does not work 有什么方法可以将信号从另一个进程发送到线程吗? - Is there any way to send signal to a thread from another process? Recv-Q +发送-Q&gt;写入字节 - Recv-Q+Send-Q>write bytes
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM