【问题标题】:Unexpected output in IPC using sockets使用套接字的 IPC 中的意外输出
【发布时间】:2017-01-24 22:47:23
【问题描述】:

我正在尝试编写一个最多可以处理 5 个并发客户端的服务器。
每当客户端成功连接到服务器并且客户端数量小于或等于 5 时,服务器会发送欢迎消息,生成一个 5 位唯一随机数用于标识该客户端,将此数字发送给客户端并打印控制台中的数字。如果客户端数量趋于大于 5,则对于每个新请求,它只会向客户端发送消息“超出连接限制”并关闭连接。
客户端只是打印服务器发送的消息。
我面临的问题是,随机数没有正确传播到客户端。客户端打印与服务器生成的相同数字的次数很少,但客户端仅打印 0 的次数很少(作为存储传入值的变量该随机数被初始化为 0)。
这可能是什么原因?

以下是客户端和服务器的代码: 服务器:

/* A simple server in the internet domain using TCP
   The port number is passed as an argument 
   This version runs forever, forking off a separate 
   process for each connection
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <stdlib.h>

void dostuff(int); /* function prototype */
void write_once (int sock);
void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno, pid, count = 0;
     socklen_t clilen;
     struct sockaddr_in serv_addr, cli_addr;

     if (argc < 2) {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);
     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");
     listen(sockfd,5);
     clilen = sizeof(cli_addr);
     while (1) {
         newsockfd = accept(sockfd, 
               (struct sockaddr *) &cli_addr, &clilen);
         if (newsockfd < 0) 
             error("ERROR on accept");
         pid = fork();
         count++;
         if (pid < 0)
             error("ERROR on fork");
         if (pid == 0 && count <=5 )  {
             close(sockfd);
             dostuff(newsockfd);
             exit(0);
         }
         if (pid == 0 && count >= 5 )  {
             close(sockfd);
             write_once(newsockfd);
             exit(0);
         }
         else close(newsockfd);
     } /* end of while */
     close(sockfd);
     return 0; /* we never get here */
}

/******** DOSTUFF() *********************
 There is a separate instance of this function 
 for each connection.  It handles all communication
 once a connnection has been established.
 *****************************************/
void dostuff (int sock)
{
   int n;
   char buffer[256];

   bzero(buffer,256);
   n = write(sock,"Welcome\n",8);
   if (n < 0) error("ERROR writing to socket");

   srand((unsigned int)time(NULL));
   int r = rand() % 90000 + 10000; 
   int converted_r = htonl(r);

   n = write(sock, &converted_r, sizeof(converted_r));
   if (n < 0) error("ERROR writing to socket");
   printf("%d\n", r);
}

void write_once (int sock)
{
   int n;
   char buffer[256];   
   bzero(buffer,256);
   n = write(sock,"Connection Limit Exceeded!!",28);
   if (n < 0) error("ERROR writing to socket");
}

客户:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
    int received_int = 0;

    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
        error("ERROR connecting");

    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%s\n",buffer);

    n = read(sockfd, &received_int, sizeof(received_int));
    if (n < 0) 
         error("ERROR reading from socket");
    printf("%d\n", ntohl(received_int)); 
    close(sockfd);
    return 0;
}

Reference

【问题讨论】:

  • TCP 函数读取(或写入)可能返回 0,您的代码无法正确处理。它们返回的字节数可能少于请求的字节数,这不是错误。在这种情况下,再次调用该函数,直到您收到所需的字节数。另见stackoverflow.com/questions/666601/…

标签: c sockets


【解决方案1】:

问题在于 TCP 是面向流的协议,而不是面向数据包的协议。所以可能会发生这种情况

  • 客户端的第一个read() 读取服务器的第一个write() 发送的内容(“欢迎”)
  • 客户端的第二个read() 读取服务器的第二个write() 发送的内容(您的号码)

这是您所期望的,有时也会发生。

但是,也有可能是客户端同时读取了服务器的两个写入数据!这通常发生在

  • 服务器将两次写入聚合到单个 tcp 数据包
  • 或者客户端在两个有数据的tcp段都到达后读取数据

您无法确定会发生什么,也不能依赖任何特定行为。

如何解决这个问题完全取决于您的协议。如果第一条消息总是“Welcome\n”,那么首先尝试只读取 8 个字节。如果您碰巧读取了 n sizeof(received_int)字节,同时观察接收到的实际字节数。

如果消息是可变长度的,您将不得不使用某种框架,例如前面的长度字节或类似的东西。

【讨论】:

    猜你喜欢
    • 2014-07-24
    • 2014-08-31
    • 1970-01-01
    • 2012-03-15
    • 2011-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多