在TCP连接中,当双方同时发送时,数据包丢失
我正在使用tcp连接进行编码。但我观察到,当双方发送/接收消息时,数据包都会丢失。我不知道这里出了什么问题 下面是一个最起码的例子。我使用gcc(没有标志/选项)进行编译。 在for循环中,双方每次都向对方发送128字节的消息。它包含一个序列num(第一个字节)。但似乎有时发送的信息总是丢失 服务器:在TCP连接中,当双方同时发送时,数据包丢失,c,sockets,networking,tcp,C,Sockets,Networking,Tcp,我正在使用tcp连接进行编码。但我观察到,当双方发送/接收消息时,数据包都会丢失。我不知道这里出了什么问题 下面是一个最起码的例子。我使用gcc(没有标志/选项)进行编译。 在for循环中,双方每次都向对方发送128字节的消息。它包含一个序列num(第一个字节)。但似乎有时发送的信息总是丢失 服务器: #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <stdlib.h>
void error(const char *msg)
{
printf("%s\n",msg);
exit(0);
}
FILE* server_open_socket(int portno)
{
int sockfd, newsockfd;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
int on=1;
if((setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr,
&clilen);
if (newsockfd < 0)
error("ERROR on accept");
close(sockfd);
FILE *stream;
stream = fdopen(newsockfd, "wb+");
printf("connected Server at %d\n",portno);
return stream;
}
int main()
{
FILE *S;
S=server_open_socket(12345);
char sbuffer[128],rbuffer[128];
for(int i=0;i<128;i++)
sbuffer[i]=0;
for(int i=0;i<256;i++)
{
rbuffer[0]=0;
sbuffer[0]=1+i%255;
int a=fwrite(sbuffer,sizeof(unsigned char),128,S);
int b=fread(rbuffer,sizeof(unsigned char),128,S);
if((a!=128)||(b!=128))
printf("Network error!\n");
if(rbuffer[0]!=sbuffer[0])
{
printf("msgerror! %d %d\n",rbuffer[0],sbuffer[0]);
exit(0);
}
}
}
客户:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <stdlib.h>
void error(const char *msg)
{
printf("%s\n",msg);
exit(0);
}
FILE* client_open_socket(int portno)
{
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname("localhost");//gethostbyname(argv[1]);
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
while(connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) == -1) {
sleep(1000);
}
FILE *stream;
stream = fdopen(sockfd, "wb+");
printf("connected Clinet at %d\n",portno);
return stream;
}
int main()
{
FILE *S;
S=client_open_socket(12345);
char sbuffer[128],rbuffer[128];
for(int i=0;i<128;i++)
sbuffer[i]=0;
for(int i=0;i<256;i++)
{
rbuffer[0]=0;
sbuffer[0]=1+i%255;
int a=fwrite(sbuffer,sizeof(unsigned char),128,S);
int b=fread(rbuffer,sizeof(unsigned char),128,S);
if((a!=128)||(b!=128))
printf("Network error!\n");
if(rbuffer[0]!=sbuffer[0])
{
printf("msgerror! %d %d\n",rbuffer[0],sbuffer[0]);
exit(0);
}
}
}
msgerror! 3 2
似乎缺少来自服务器的以“2”开头的消息
如果我让双方按顺序发送消息(服务器发送然后侦听,客户端侦听然后发送),这个问题似乎就消失了。但我也不知道为什么
当涉及多个参与方时,例如,三台机器/主机/参与方,每台机器/主机/参与方对另一方都有一条不同的消息:总共6条消息。我该如何处理这样的任务?你完全把stdio库搞糊涂了 不能在同一套接字上同时在两个方向使用缓冲流。缓冲流机制的设计考虑了一个特定的模型:磁盘上普通文件的模型。插座不是那样的。也就是说,该库是为一种模型设计的,其中“写入”端和“读取”端共享一个底层文件指针,并且您可以同时读取和写入数据的同一区域 考虑一下,如果您在磁盘上操作一个普通文件,那么这将如何工作,并将其与套接字进行对比。您可以从“文件”中读取128个字节。这会导致库实际读取(如在
read(2)
系统调用中)至少128个字节,但它会尝试读取比正常情况下更多的字节,直到达到某个较大的缓冲区大小。然后,它会将128字节传输到缓冲区,并将其内部文件偏移量设置为128。如果然后写入128字节,我相信stdio库将覆盖其内部缓冲区中从偏移量128开始的所有字节,长度为128
如果它最初读取的数据超过128字节,那么它现在需要重新定位底层文件指针,然后才能将数据“刷新到磁盘”(即调用write(2)
)。我不确定它到底会写多少,但它会尝试在其内部缓冲区中保持数据的一致视图——但它再次假设了一个与双向套接字数据流完全不同的模型。因此,在执行写入之前,它将尝试重新定位文件指针以准备更新文件,因为“文件”实际上是一个套接字,lseek(2)
系统调用失败(这解释了您看到的“网络错误”)
您可以在套接字上创建两个单独的缓冲流,一个用于读取(fdopen(sockfd,“rb”)
),另一个用于写入(fdopen(sockfd,“wb”)
)。我不知道库标准化是否能保证这一点——可能不能——但由于每个缓冲流都是完全独立的,而且您只从一个流读取数据,而只向另一个流写入数据,因此库不需要以影响两个方向的方式调整其文件偏移量,该库可以将每个“流”几乎完全视为磁盘上正在缓慢增长的文件
如果您尝试这样做,则需要添加一些
fflush(3)
调用,以强制库实际将缓冲数据发送到对等方。因为否则,它不知道它需要这样做。注意:'SOCK_STREAM'是写入还是读取失败?您永远不会检查fdopen()
@jwdonahue的结果如果我不检查消息的正确性,程序可以继续,直到其中一方脱机(另一方给出“fread错误”)。因此我认为fdopen()不会失败。如果不检查返回值,就不知道是否出了问题。这是非常弱的代码。您还应该独立检查写入和读取结果,以便调试此问题。非常感谢。你的回答使我摆脱了这个问题。
msgerror! 3 2