截断缓冲区

时间:2013-10-07 19:30:35

标签: c sockets

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

我正在输入客户端程序的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);

2 个答案:

答案 0 :(得分:2)

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

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

问题:网址是可变长度的。接收器如何知道何时读取它? 解决方案:发送前的长度

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

问题:网址长度可以是可变长度(“1”,“12”,“1234”)。接收器如何知道何时读取它? 解决方案:我们不是来过某个地方......

这种递归问题有两种方法:

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

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

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

RX:

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

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

答案 1 :(得分:0)

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

即使发送方可能已经立即发送了所有数据,但这并不能保证您一次看到所有数据。您必须在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;
     }
}

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

请参阅here以获取解释。