我一直在看这几个小时。我已经尝试了所有我能想到的东西,坦白说它没有意义。我主动发送和接收套接字没有问题,但只要我将数据更改为不同的消息,相同的样式,它就会停止接收。我正在使用TCP。我有一个管理器进程发送带有表数据的N路由器消息。我后来发送一个数据包,相同的样式,接收它,然后停止接收....代码返回到循环的顶部,但只是没有得到任何更多的数据。
哦我正在使用的网络代码是beejs TCP服务器客户端代码的复制和粘贴。 http://beej.us/guide/bgnet/output/html/multipage/clientserver.html
经理线程,这部分工作
for(vector< vector<int> >::iterator it = table.begin(); it!=table.end(); ++it ){
vector< int > d = *it;
for(vector<int>::iterator itA = d.begin(); itA!=d.end(); ++itA ){
cout << "Sending... "<< *itA << endl;
s <<*itA<<" ";
}
if (send(new_fd, s.str().c_str(), 13, 0) == -1)
perror("Serv:send");
sleep(2);
logs << "Sent to router " << i <<":\n" << s.str();
writeLog(logs.str().c_str());
s.str("");
logs.str("");
}
s<<"done";
if (send(new_fd, s.str().c_str(), 13, 0) == -1)
perror("Serv:send");
writeLog(s.str().c_str());
管理2,只有第一条消息通过
for(vector <vector <int > >::iterator it = toSendPackets.begin(); it != toSendPackets.end(); ++it){
sleep(3);
vector<int> tsp = *it;
int a,b,c = 0;
for(vector<int>::iterator itr = tsp.begin(); itr != tsp.end(); ++itr){
if(c==0){
a = *itr;
}
if(c==1){
b = *itr;
}
c++;
}
ss.str("");
ss << a << " " << b;
for(int i = 0; i < numN; i++){
int curSoc = socketList[i];
stringstream sl;
sl<<"sent:"<< ss.str().c_str();
cout << "sending.. " << ss.str() << " to " << i << endl;
if (send(curSoc, "HOP", strlen("HOP")+1, 0) == -1)
perror("Serv:send");
sleep(2);
if (send(curSoc, ss.str().c_str(), strlen(ss.str().c_str())+1, 0) == -1)
perror("Serv:send");
writeLog(sl.str().c_str());
sleep(1);
}
}
路由器代码。
上面的经理代码和经理代码2都发送到这部分代码。 它获得了第一次发送,在这种情况下是“HOP”然后什么都没有?我删除了HOP数据包解析,因此它应该只说明某些内容被读取。
if(tid == 0){// TCP
stringstream s;
bool proc = true;
while(!doneFlag){
proc = true;
cout << "TCP RECEIVING... " << endl;
int numbytes = 0;
while(numbytes==0){
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recvROUTERThread0");
exit(1);
}
}
buf[numbytes] = '\0';
numbytes = 0;
if(strcmp("Quit",buf)==0){
writeLog("Quit read",outName);
doneFlag = true;
close(net.sockfd);
floodUDP("Quit");
pthread_exit(NULL);
}
else if(strcmp("HOP",buf)==0){
cout << "HOP READ" << endl;
numbytes = 0;
while(numbytes==0){
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
perror("recvROUTERThread0");
exit(1);
}
}
s << id << "R: Receiving a routing command! " << buf;
cout << s.str().c_str() << endl;
writeLog(s.str().c_str(),outName);
HOPpacket hpo = genHopOrig(s.str().c_str());
if(hpo.s == atoi(id)){
printHOP(hpo);
// cout << "PACKET " << pr << endl;
stringstream sl;
char* hop = generateHopPacket(hpo);
sl << "Generating HOP packet and sending.. " << hop;
writeLog(sl.str().c_str(),outName);
sendHOP(hop);
}
}
else{
cout << "Table row data from manager" << endl;
s.str("");
s << id << "R: MANAGER MESSAGE: " << buf << endl;
cout << s.str() << endl;
writeLog(s.str().c_str(),outName);
int intID = atoi(id);
vector <int> tr = processTR(buf,intID,basePN);
table.push_back(tr);
}
}
}
我的输出。在这种情况下,有10台路由器在运行。注意我没有更改我的打印,表明它正在发送HOP然后0 5 ..
sending.. 0 5 to 0
HOP READ
WRITTING Manager log:12-11-23::4:6:26:
sent:0 5
sending.. 0 5 to 1
HOP READ
WRITTING Manager log:12-11-23::4:6:29:
sent:0 5
sending.. 0 5 to 2
HOP READ
WRITTING Manager log:12-11-23::4:6:32:
sent:0 5
sending.. 0 5 to 3
HOP READ
WRITTING Manager log:12-11-23::4:6:35:
sent:0 5
sending.. 0 5 to 4
HOP READ
WRITTING Manager log:12-11-23::4:6:38:
sent:0 5
sending.. 0 5 to 5
HOP READ
WRITTING Manager log:12-11-23::4:6:41:
sent:0 5
sending.. 0 5 to 6
HOP READ
WRITTING Manager log:12-11-23::4:6:44:
sent:0 5
sending.. 0 5 to 7
HOP READ
WRITTING Manager log:12-11-23::4:6:47:
sent:0 5
sending.. 0 5 to 8
HOP READ
WRITTING Manager log:12-11-23::4:6:50:
sent:0 5
sending.. 0 5 to 9
HOP READ
WRITTING Manager log:12-11-23::4:6:53:
sent:0 5
sending.. 3 9 to 0
WRITTING Manager log:12-11-23::4:6:59:
sent:3 9
sending.. 3 9 to 1
WRITTING Manager log:12-11-23::4:7:2:
sent:3 9
sending.. 3 9 to 2
WRITTING Manager log:12-11-23::4:7:5:
sent:3 9
sending.. 3 9 to 3
WRITTING Manager log:12-11-23::4:7:8:
sent:3 9
sending.. 3 9 to 4
WRITTING Manager log:12-11-23::4:7:11:
sent:3 9
sending.. 3 9 to 5
WRITTING Manager log:12-11-23::4:7:14:
sent:3 9
sending.. 3 9 to 6
WRITTING Manager log:12-11-23::4:7:17:
sent:3 9
sending.. 3 9 to 7
WRITTING Manager log:12-11-23::4:7:20:
sent:3 9
sending.. 3 9 to 8
WRITTING Manager log:12-11-23::4:7:23:
sent:3 9
sending.. 3 9 to 9
WRITTING Manager log:12-11-23::4:7:26:
sent:3 9
答案 0 :(得分:2)
当您recv
数据时,存在问题,TCP是基于流的套接字而不是基于消息的套接字,因此如果您使用:
send( sock, buf1, len1, 0 ); // Send HOP, since it is small, you OS merge this
send( sock, buf2, len2, 0 ); // with next send!
然后尝试使用recv
接收数据,但不能保证您在两次单独的recv
调用中收到数据,因此您可以在一次调用中收到两个已发送的缓冲区recv
:
recv( sock, buf, len, 0 ); // This may receive both buffers in one call
因此,对于已在第一次通话中收到的数据,您的recv
的下一次通话将被屏蔽!此外,当您发送大缓冲区时,它们可能是另一个问题,然后recv
可能会收到的数据少于使用send
传递的单个邮件。
您必须定义一个协议,用于定义接收流中的消息结束,然后根据该协议接收数据。例如,您可以先发送消息长度或定义指示消息结束的内容(例如\0
或\r\n
)。
很抱歉我对错误的描述不完整。在您的评论中,您说您已经增加了HOP
邮件大小!但它肯定不是一个好的做法,也增加了大小是如此之小,以至于永远不会迫使操作系统立即发送它(实际上没有一定的大小迫使操作系统这样做)。如果您希望操作系统立即发送数据,您应该使用TCP_NO_DELAY
选项禁用Nagle算法,但在此之前请先查看How do I use TCP_NODELAY?。 执行此操作不是一个好习惯,除此之外,这样做会导致您在调用send
时立即发送数据包,但它永远不会强迫接收方的操作系统单独接收消息!! 那么什么这样做的正确方法是什么?
我详细解释了这个问题:
// I don't know exact value of MAXDATASIZE but I will assume it is 128
char buf[ MAXDATASIZE ];
int numbytes = recv( sock, buf, MAXDATASIZE, 0 );
if( numbyte == -1 ) {
// Handle error
}
// I assume HOP_MSG is a defined constant that contain value of HOP message
if( strcmp(buf, HOP_MSG) == 0 ) { // <-- (1)
while( (numbytes = recv(sock, buf, MAXDATASIZE, 0)) != -1 ) { // <-- (2)
if( numbytes == 0 ) break;
}
if( numbytes == -1 ) {
// Handle error
}
}
但是等等!在标有(1)
的行中,我假设recv
完全阅读HOP_MSG
且仅HOP_MSG
,但为什么?正如我之前所说{{1是一个流协议,它没有消息边界,所以它只能读取2个字节!!或者它读TCP
(肯定超过1KB
,所以我该怎么办?
工作答案如下:
HOP_MSG
通过调试此代码,您一定会理解其目的,int receive_till_zero( SOCKET sock, char* tmpbuf, int& numbytes ) {
int i = 0;
do {
// Check if we have a complete message
for( ; i < numbytes; i++ ) {
if( buf[i] == '\0' ) {
// \0 indicate end of message! so we are done
return i + 1; // return length of message
}
}
int n = recv( sock, buf + numbytes, MAXDATASIZE - numbytes, 0 );
if( n == -1 ) {
return -1; // operation failed!
}
numbytes += n;
} while( true );
}
void remove_message_from_buffer( char* buf, int& numbytes, int msglen ) {
// remove complete message from the buffer.
memmove( buf, buf + msglen, numbytes - msglen );
numbytes -= msglen;
}
void main() {
SOCKET s;
char buf[ MAXDATASIZE ];
int numbytes = 0, msglen;
// Initialize socket and connect to server, you already do that
while( true ) {
msglen = receive_till_zero( s, buf, numbytes );
if( msglen == -1 ) {/* Handle error */}
if( !strcmp(buf, HOP_MSG) ) {
remove_message_from_buffer( buf, numbytes, msglen );
msglen = receive_till_zero( s, buf, numbytes );
if( msglen == -1 ) {/* Handle error */}
std::cout << "Message received from server: " << buf << std::endl;
remove_message_from_buffer( buf, numbytes, msglen );
}
}
}
假设缓冲区中已有一些待处理数据,从之前调用receive_till_zero
,因此它将首先检查是否有完整的消息在缓冲区中是否存在,并且它也永远不会假设仅通过一次调用recv
来完成接收数据,因此它将在循环中调用recv
,直到它在缓冲区中看到recv
。在我们完成缓冲区中的数据之后,我们调用\0
来吃掉那些数据而只吃那些数据,而不仅仅是从缓冲区的开始接收,因为它们可能已经在缓冲区中有一些数据了。
正如您所看到的代码有点复杂,为了获得更好的编程模型和更好的C ++代码,您可以使用boost::asio设计非常好并且与C ++和remove_message_from_buffer
完美配合强>