0

我正在尝试解决一个奇怪的 C 编程问题。

我在客户端程序上输入 URL,然后将该 URL 传输到服务器程序。唯一的问题是,当服务器程序接收到 URL 时,它缺少前两个字符。因此,如果 URL 是http://www.google.com,则服务器报告接收的是“tp://www.google.com”。

奇怪的是,这不是部分发送问题。我正在检查发送的字节数,它声称发送了整个消息。问题是在接收端它只是声称它正在获取一小部分数据。接收方报告它收到的消息长度为-2。这是接收端的代码:

  printf("%s \n", "Connected. Receive length of URL to wget.");
  if ((messageSize = recv(acceptDescriptor, &urlLength, sizeof (int), 0)) == -1) {
    perror("recv URL length");
    exit(1);
  }
  urlSizeInt = atoi(urlLength);
  char url[urlSizeInt];
  printf("%s %d \n", "urlSizeInt: ", urlSizeInt);
  printf("%s \n", "Receive URL to wget.");

  if((messageSize = recv(acceptDescriptor, &url, 13, 0)) == -1) {
    perror("recv URL");
    exit(1);
  }

发送代码:

  printf("%s \n", "Connected");
  //connected to first stepping stone in the chain.
  //transfer the length of the URL
  if (send(socketDescriptor, urlLengthStr, strlen(urlLengthStr), 0) == -1){
    perror("send URL Length");
    exit(0);
  }

  //transfer the URL
  printf("%s %d \n", "strenlen(url): ",strlen(url));
  printf("%s %s \n", "url: ",url);
  int sent;
  int totalSent=0;
  if((sent=send(socketDescriptor, url, strlen(url), 0))==-1){
      perror("send URL");
      exit(0);
    }

  printf("%s %d \n", "sent: ",sent);


Send Output:
Connected
strenlen(url):  13
url:  http://www.cs
sent:  13

Receive Output:
Connected. Receive length of URL to wget.
urlSizeInt:  13
Receive URL to wget.
messageSize:  11
URL Received:  tp://www.cs

将长度编码为用于发送的字符的代码:

char* url = "http://www.cs";
int urlLength  = strlen(url);
char* urlLengthStr;
sprintf(urlLengthStr, "%d", urlLength);
4

2 回答 2

2

感谢您发布完整的代码。问题在于您发送 UrlLength 的方式。因为您总是recv sizeof(int)字节,所以您的第一次读取会消耗已发送 URL 的第一个字节。

从头开始 - 假设您不发送 URL 长度。

问题:URL 是可变长度的。接收者怎么知道它什么时候读完了? 解决方法:发送之前的长度

这很好,除非您将长度编码为字符串,因为这会引入另一个问题

问题:URL 长度可能是可变长度(“1”、“12”、“1234”)。接收者怎么知道它什么时候读完了? 解决方案:我们以前不是来过这里吗...

有几种方法可以解决这个递归问题:

解决方案 a:将 URL 长度编码为固定大小的字段。(您可以只发送 int 的二进制表示,但要注意字节顺序问题 - 或者您可以将其编码为固定宽度的 ascii 字段,例如“00000124”

TX(忽略 JimR 提到的字节排序问题)

  int urlLength = strlen(url);
  send(socketDescriptor, &urlLength, sizeof(int), 0)

接收:

  int urlLength;
  recv(socketDescriptor, &urlLength, sizeof(int), 0)

解决方案 b:使用终止字符(通常为 null 或换行符)来指示 URL 的结尾。只需循环读取字节,直到到达终止符。这也解决了您可能会遇到的“部分接收”问题。

于 2013-10-07T20:00:04.033 回答
0

当您从套接字读取时,您正在读取流,而不是文件。有关编写网络代码的良好指南,请参见此处。

即使发送方可能一次发送了所有数据,但这并不能保证您会立即看到所有数据。您必须循环recv,考虑到每次调用收到的字节数。如果recv返回0套接字已关闭或出现错误,您将不会从该套接字接收更多数据。

考虑到这些事情......有点伪代码,我没有测试这个,但希望它能给你这个想法:

int expectedLength = readLengthFromSocket( socket, sizeof( int ) );
int bytesRead = 0;
char buffer[expectedLength];
bytesRead = recv( socket, buffer, ... );
runningLength = 0;
if( bytesRead < 1 )
    // Socket closed or there was an error, handle that here
else
{    runningLength += bytesRead;
     while( runningLength < expectedLength )
     {
         bytesRead = recv( socket, buffer + runningLength, ... );
         if( bytesRead < 1 )
            // Socket closed or there was an error, handle that here
            break;
         else
             runningLength += bytesRead;
     }
}

请注意,通过网络读取和写入、 、intlong无符号变体通常需要字节交换。读取字节缓冲区不会。long longshort

请参阅此处以获取解释。

于 2013-10-07T20:23:53.820 回答