【发布时间】:2014-05-28 20:27:47
【问题描述】:
我正在尝试实现一个有效的 HTTP 客户端-服务器应用程序,只是为了练习网络编程。
这两个程序必须遵循这个基本算法:
客户端 - 发送 GET 请求
服务器 - 发送“+OK\r\n”
服务器 - 以字节为单位发送文件大小
服务器 - 发送文件
客户端 - 发送 ACK
我在读取部分遇到了很多麻烦,可能是因为我对流执行了一些脏读操作。
这些是我正在使用的 2 个阅读功能:
/* Reads a line from stream socket s to buffer ptr
The line is stored in ptr including the final '\n'
At most maxlen chasracters are read*/
int readline (SOCKET s, char *ptr, size_t maxlen)
{
size_t n;
ssize_t nread;
char c;
for (n=1; n<maxlen; n++)
{
nread=recv(s, &c, 1, 0);
if (nread == 1)
{
*ptr++ = c;
if (c == '\n')
break;
}
else if (nread == 0) /* connection closed by party */
{
*ptr = 0;
return (n-1);
}
else /* error */
return (-1);
}
*ptr = 0;
return (n);
}
和:
int readNumber(SOCKET s, long *num, int maxRead)
{
size_t n;
ssize_t nread;
int totRead;
long number=0;
for (n=1; n<maxRead+1; n++)
{
nread=recv(s, &number, sizeof(number), 0);
if (nread == sizeof(number))
{
totRead+=nread;
*num = number;
}
else if (nread == 0) /* connection closed by party */
{
*num = 0;
return (n-1);
}
else /* error */
{
printf("nread = %d\n", nread);
return (-1);
}
}
return (totRead);
}
这是我收到+OK消息的主要sn-p,然后是文件大小:
memset(rbuf,0,sizeof(rbuf)); //rbuf is the buffer where is store the read
printf("waiting for response...\n");
result = readline(s, rbuf, sizeof(rbuf)); //reading function is above
printf("Byte read(okMsg) = %d\n", result);
if (result <= 0)
//ERROR MANAGEMENT
{
printf("Read error/Connection closed\n");
closesocket(s);
SockCleanup();
exit(1);
}
else
{
long fileLength=0;
unsigned char *fBuf;
//RECEIVE OK
if(!strcmp(rbuf,"+OK\r\n"))
{
puts("+OK\n");
//RECEIVE FILE LEN
int nw = readNumber(s, &fileLength, 1); //reading function is above
printf("Byte read(fDim) = %d\n", nw);
printf("File is %ld bytes long\n", fileLength);
if(nw >0)
{
// RECEIVE FILE
}
}
}
当我发送“+OK\r\n”字符串时,服务器告诉我它发送了 8 个字节,但是当我阅读时,我发现 '\0' 字符仅在 6 个字节之后。
顺便说一句,它正确读取了消息,但是当我尝试读取文件大小(很长)时,它给了我一个错误的数字。
我的观点是流缓冲区很脏,我正在读取不属于文件大小的 2 个字节,但我不明白为什么会发生这种情况。
如果我不够清楚,请询问我更多信息。
已解决:
谢谢大家的答案!!!
你让我有正确的心态去理解哪里出了问题。
看起来问题是服务器中的这个声明:
char *okMsg = "+OK\r\n";
而不是
char okMsg[] = "+OK\r\n";
这导致我出现未定义的行为。
【问题讨论】:
-
您必须记住 TCP 是一种流协议。这意味着对
recv的调用可能会返回少于请求的字节数。这是您的readNumber函数可能无法按预期工作的一个原因。但更大的原因是当您使用未初始化的局部变量(更具体地说是totRead)时,其中包含undefined behavior。 -
"+OK\r\n" 是 5 个字节,而不是 8 个字节,因此您可能想弄清楚为什么您的服务器给您一个错误的报告。
-
我一直相信,每当我不初始化变量时,它都会隐式初始化为零,请问您能否举一个我的代码中未定义行为的示例?
-
在函数中声明一个局部变量并打印它,这就是验证行为所要做的一切。全局变量和静态变量是零初始化的,但不是普通的局部变量。
-
我想指出您的 readline 循环非常简单,但也可能是读取数据性能最差的方式。您正在为每个角色调用 recv 。那是系统调用。系统调用会占用大量 CPU 周期。更好的方法是使用缓冲并一次读取 4K 或更多的套接字。