为什么套接字未正确处理此缓冲区大小
Why this buffer size is not properly handled by sockets?
当我BUF_SIZE设置为 10000 时,工作正常,但 50000 不能。我只是尝试将数据从客户端发送到服务器并测试缓冲区大小,但我发现在某些大小下,应用程序无法正常工作。
为什么??我该如何解决它?
例如,我运行服务器和客户端,第一次尝试工作正常,但是当我重新运行客户端时,开始交付问题。
这是服务器:
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <signal.h>
#define BUF_SIZE 50000
using namespace std;
void manejador(int signo);
int main()
{
int sservice, sclient,l,nbytes_read, err, nbytes_sent;
bool end;
char buf[BUF_SIZE];
struct sockaddr_in sin, clientfsin;
pid_t pid;
int status;
sservice=socket(PF_INET,SOCK_STREAM, 0); /*Open the socket*/
if(sservice == -1)
{
perror("Server. Socket: ");
exit(-1);
}
sin.sin_family = AF_INET; /*ARPANET address family*/
sin.sin_addr.s_addr = INADDR_ANY; /*Accept connections on any Interface*/
sin.sin_port = htons(4000); /*Service TSAP > 1023. CHANGE IT!*/
/*Register the server in the system*/
err=bind(sservice, (struct sockaddr*)&sin, sizeof(sin));
if(err == -1)
{
perror("Server. bind: ");
exit(-1);
}
/*Up to 5 waiting connections*/
err = listen(sservice,5);
if(err == -1)
{
perror("Server. Listen: ");
exit(-1);
}
/* Receiving requests loop */
for(;;)
{
/*Accept a connection from a client*/
l = sizeof(clientfsin);
sclient = accept(sservice,(struct sockaddr *)&clientfsin, (socklen_t*) &l);
if(sclient == -1)
{
perror("Server. Accept: ");
continue;
}
signal(SIGCHLD,manejador); //Quitar si ponemos waitpid
pid = fork();
if(pid == -1){
printf("Error al crear el proceso hijon");
exit(0);
}
if(pid){
close(sclient);
//waitpid(pid,&status,0); //Descomentar si usamos waitpid
}else{
close(sservice);
/*Give the service*/
end = false;
int i=1;
while(!end && (i<=10))
{
nbytes_read=recv(sclient,(char *)buf,sizeof(buf),0);
if (nbytes_read > 0)
{
buf[nbytes_read]=' ';
//cout << "SERVER>Server received: " << buf << endl;
printf("Recepcion <%i>: Se han recibido <%i> bytes del clienten",i,nbytes_read);
cout.flush();
err = 0;
//sprintf(buf,"%s_server",buf);
nbytes_sent = send(sclient,(char *)buf,sizeof(buf),0);
printf("Envio <%i>: Se han enviado <%i> bytes al clienten",i,nbytes_sent);
i++;
}
else
{
perror("Sever. Receive/read: ");
end=true;
err = -1;
}
}
if(err >= 0)
cout << "SERVER>Cliente Atendido" << endl;
else
cout << "SERVER>Finalizacion incorrecta del cliente" << endl;
/*Never forget to close a socket!*/
close(sclient);
exit(0);
}
}
close(sservice);
printf("Fin server");
} /*main()*/
void manejador(int signo) //comentar si usamos waitpid
{
int estado;
wait(&estado);
}
这是客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <sys/time.h>
#define BUF_SIZE 50000
using namespace std;
int main()
{
int sock, err;
bool end;
char buf[BUF_SIZE];
struct sockaddr_in sout;
sock=socket(PF_INET,SOCK_STREAM, 0); /*Open the socket*/
if(sock == -1)
{
perror("Client. Socket: ");
exit(-1);
}
sout.sin_family = AF_INET; /*ARPANET address family*/
sout.sin_addr.s_addr = inet_addr("127.0.0.1"); /*Which server?*/
sout.sin_port = htons(4000); /*Output port*/
/*Connect to the server*/
err = connect(sock,(struct sockaddr *)&sout, sizeof(sout));
if(err == -1)
{
perror("Client. Connect: ");
exit(-1);
}
end = false;
double t1,t2;
while(!end)
{
/*Ask for the service*/
//cout << endl << "CLIENT> Send a message...: " ; cout.flush();
//cin.getline(buf, 128);
int i=0;
for(i=0;i<10;i++){
timeval tim;
gettimeofday(&tim, NULL);
t1=tim.tv_sec+(tim.tv_usec/1000000.0);
err = send(sock,(char *)buf,sizeof(buf),0);
if(err == -1)
{
perror("Client. Send/write: ");
exit(-1);
}
printf("Envio <%i>: Se han enviado <%i> bytesn",i+1,err);
gettimeofday(&tim, NULL);
t2=tim.tv_sec+(tim.tv_usec/1000000.0);
printf("%.6lf para el envio de <%i>n", t2-t1,i+1);
err = recv(sock,(char *)buf,sizeof(buf),0);
printf("Recepcion <%i>: Se han recibido <%i> bytesn",i+1,err);
//cout << "CLIENT> Server response: " << buf;
cout.flush();
}
end=true;
}
close(sock);
} /*main()*/
题外话:很抱歉西班牙语的评论;)
错误:
1) 您的服务器不会尝试确保它已收到完整的消息。
2) 您的服务器发送 50,000 字节,无论它实际需要发送多少字节。
3) 如果您的服务器实际读取 50,000 字节,则尝试添加终止零将溢出缓冲区。
3) 您的客户端发送 50,000 个不确定字节。
4) 客户端忽略它收到的字节数。
5) 客户端不确保它实际接收到服务器发送的终止零字节。
但你最大的错误是:你没有合理的协议。如果消息以零字节结尾,为什么要发送 50,000 字节?如果你的消息总是 50,000 字节,为什么接收方不尝试接收 50,000 字节?
我会给你我给每个TCP程序员的标准建议:从开发一个协议开始并记录它。协议应指定谁发送以及何时发送。协议应指定消息的框架方式。协议应指定有效和无效消息的规则。协议应指定如何检测和处理死连接。等等。
正确记录协议需要一个小时左右,但这是值得的。没有一个,很难说我上面的哪些错误真的是错误。(例如,也许您真的应该始终发送 50,000 字节,即使消息只是"hi"。服务器是否应该通过字节计数或终止零来查找消息的结尾?等等。
我运行了你的程序。问题在于您对recv
工作方式的期望。您希望接收器始终能够在一次读取中读取所有BUF_SIZE
字节。如果这是您的期望,则应在recv
调用的最后一个参数中设置 MSG_WAITALL
标志。尽管在阻塞 I/O 时,send
调用将保持阻塞状态,直到发送所有字节,但默认情况下,对于 recv
来说并非如此。无论套接字输入队列中有多少数据,它都会接收,因此recv
可能比没有 MSG_WAITALL
标志的预期短。
至于为什么较小的BUF_SIZE
值有效而较大的值不起作用,这可以解释为套接字输入队列的大小。您可以尝试将setsockopt
与SO_RCVBUF
选项一起使用,将其设置为匹配或超过您的BUF_SIZE
,看看它是否适合您。但是,在现实生活中,网络条件将决定输入缓冲区是否保持满,因此短读取只是程序应该处理的事情。
答案的其余部分解决了代码中的一些样式问题和一种"偏离一"错误。
我注意到您正在堆栈中创建一个相当大的数组。您可以考虑动态分配它。一种方法是使用向量。
std::vector<char> buf;
buf.resize(BUF_SIZE);
nbytes_read = recv(sclient, &buf[0], buf.size(), 0);
另一件需要注意的事情(@Linux_iOS.rb.cpp.c.lisp.m.sh注意到)是,如果nbytes_read
的值BUF_SIZE
,那么试图NUL
终止输入是错误的做法。它将访问缓冲区区域之外的数据。如果你真的必须NUL
终止,那么对于向量,你可以使用 push_back
.
if (nbytes_read == buf.size()) buf.push_back(' ');
else buf[nbytes_read] = ' ';
但是您应该发现设置缓冲区的大小更容易。
buf.resize(nbytes_read);
然后,当您回显数据时,将只回显读取的内容。
send(sclient, &buf[0], buf.size(), 0);
- arm-none-eabi-g++ 不能使用 -flto 正确处理弱别名
- 如何避免 boost 的野兽body_limit错误并正确处理大消息
- 如何正确处理渲染大小为 >= 2B 的 utf-8 字符?
- 是否正确处理了此代码异常?
- 为什么 CSpinButtonCtrl 不能正确处理大于 1000 的数字?
- 自定义迭代器:如果 a 和 b 的行为不同,如何正确处理距离计算和相等比较
- 编译器在 C/C++ 中通过指针调用函数时如何正确处理 ABI
- 如何在 Python 3 中正确处理用于嵌入的字符串
- 如何正确处理OpenSSL错误(BIO)
- 为什么我的模板专业检查不足以正确处理相应的类型
- 在子类过程中正确处理WM_PASTE
- 如何正确处理此SFINAE
- 如何在 c++ 中正确处理常量字符*
- 如何在多线程程序中使用 boost::asio 正确处理 fork()
- 类的静态实例无法在程序退出时正确处理资源删除
- 正确处理字节对齐问题 - 通过UDP在16位嵌入式系统和32位桌面之间
- 如何确保二元布尔操作员正确处理其论点的构造
- FSNF 无法正确处理字符串
- VS2010 未正确处理预处理器条件下的"includes"
- 为什么套接字未正确处理此缓冲区大小