【问题标题】:Call to recv() blocks input调用 recv() 块输入
【发布时间】:2014-09-26 23:40:17
【问题描述】:

我的套接字有问题。我有一个服务器和一个客户端。

项目的目的:

客户端/服务器连接(都互相发送消息) 客户端发送消息; 服务器读取消息; 服务器将消息发回给客户端。

但我遇到了死锁,因为客户端和服务器都在等待接收消息。代码:

Server.c

message = "Client connection handler ok\n";
write(sock , message , strlen(message));

message = "type something and i'll repeat what you type \n";
write(sock , message , strlen(message));

//Receive a message from client
while( (read_size = recv(sock , client_message , 2000 , 0)) > 0 )
{
    puts(client_message);

    //Send the message back to client
    write(sock , client_message , strlen(client_message));

    bzero(client_message,2000);
}

Client.c

//Here I print the two messages You can see server side code:
//- Client connection handler ok
//- type something and i'll repeat what you type
while( (read_size = recv(sockfd , client_message , 2000 , 0)) > 0 )
{
     printf("%s\n",client_message);
     bzero(client_message,2000);
}

在执行代码的过程中,我在客户端和服务器中都处于阻塞状态。

我该如何解决这个问题?

【问题讨论】:

  • 客户端循环永远不会写入服务器,因此从您的代码中可以明显看出服务器将坐在那里等待。然后,如果它等待数据,它也永远不会写,所以客户端也会等待。
  • 是的,但我不知道该如何解决...如果我在 while 中插入 fgets 我会阻塞循环...这不好...你怎么看我从服务器向客户端发送两条消息...如果我将fgets 放入其中,我只会收到第一个...所以我不知道如何解决这种情况...
  • 您可能想了解select() 和/或poll()。在 TCP 级别上也有 no 消息。 TCP 是面向的。要进行消息传递,您需要在/使用双向 TCP 流之上定义和实现协议。
  • 安全漏洞 - 即使您在每次调用 recv 之前将缓冲区归零,您的客户端和服务器代码都假设接收到的消息以空值结尾(最后一个字符为零字节)。我的恶意客户端(或服务器)可以向另一个节点发送一条 2000 字节的消息,最后没有空字符。简单的解决方法是将 1999 作为长度传递给 recv() ,然后在检查 read_size is >= 0 后设置 client_message[read_size] = '\0'

标签: c sockets recv


【解决方案1】:

您正在寻找select 函数。你告诉select 等待 标准输入和你的套接字。

您的客户端将如下所示(未经测试的代码):

/* This is a bit mask where each bit corresponds to a file descriptor.
   It is an in/out parameter, so you set some bits to tell select what
   to wait on, and then select sets bits in it to tell you what fd has
   data waiting for you to read. */
fd_set read_fds;

while(1){
    int fd_max = STDIN_FILENO;

    /* Set the bits for the file descriptors you want to wait on. */
    FD_ZERO(&read_fds);
    FD_SET(STDIN_FILENO, &read_fds);
    FD_SET(sockfd, &read_fds);

    /* The select call needs to know the highest bit you set. */    
    if( sockfd > fd_max ) { fd_max = sockfd; }

    /* Wait for any of the file descriptors to have data. */
    if (select(fd_max + 1, &read_fds, NULL, NULL, NULL) == -1)
    {
      perror("select:");
      exit(1);
    }

    /* After select, if an fd's bit is set, then there is data to read. */      
    if( FD_ISSET(sockfd, &read_fds) )
    {
        /* There is data waiting on your socket.  Read it with recv(). */
    }

    if( FD_ISSET(STDIN_FILENO, &read_fds )
    {
        /* The user typed something.  Read it fgets or something.
           Then send the data to the server. */
    }
}

从标准输入读取数据后不要忘记将数据发送到您的服务器。在您发布的代码中,您实际上并未向服务器发送任何数据,因此服务器不会唤醒。

【讨论】:

  • 几乎完成了......一切看起来都可以工作......只是对while(1)的疑问......当我关闭与服务器的连接时,循环继续,所以我想我需要一些东西喜欢while(connection is open)。任何想法? Ps.:不存在像msdn这样的引用来知道库中的哪些方法?
  • 是的,我会循环一个变量。例如,您可以执行诸如检测用户何时键入 /quit 之类的操作,然后更改变量以退出循环。
【解决方案2】:

一个最小的协议将由以下实现:

//Here I print the two messages You can see server side code:
//- Client connection handler ok
//- type something and i'll repeat what you type
while( (read_size = recv(sockfd , client_message , 2000 , 0)) > 0 )
{
  printf("%s\n",client_message);
  bzero(client_message,2000);

  if (0 == strcmp(client_message, "type something and i'll repeat what you type \n")
  {
    char buffer[256] = {0};
    fgets(buffer, sizeof(buffer), stdin);
    write(sockfd, buffer, strlen(buffer)+1);
  }
}

为了便于阅读,上面示例中的任何错误检查都被省略了。

如果(幸运地)接收到请求客户端输入任何内容的字节流部分(作为一个片段),则调用fgets() 从标准输入中读取一行,然后尝试通过插座。


另外请注意,从/向套接字读取/写入不一定要读取/写入很多字节,因为调用被告知要读取/写入。因此,为了确保请求的数据量已被读取/写入,代码需要围绕这些调用循环检查它们的结果,直到最终传输了请求的数据量。更多相关信息,包括此处显示如何执行此操作的答案的链接:https://stackoverflow.com/a/24536689/694576

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 2019-11-22
    • 2021-11-15
    • 1970-01-01
    • 2019-01-10
    • 1970-01-01
    • 2015-06-22
    相关资源
    最近更新 更多