因此,前几天我得到了一个相对简单的任务,那就是构建一些客户端和一些服务器代码,服务器在其中接收消息并返回其当前系统时间。这并不难,我交付并获得了一个简单的分数。
我开始对此进行更多思考,我决定着手尝试发送特定文件服务器->客户端的内容(服务器将内容发送到客户端)。当我在构建代码时,我经常在本地进行测试,并且可以按预期工作,但是当我将服务器代码上传到运行Ubuntu的服务器(哈哈)时,真正的问题出现了。启动服务器,一切正常,启动客户端,询问“ index.html”和 BAM!一半文件未收到。 服务器将其打印出来(我做到了,所以它在发送文件时就打印了文件的内容,这样我可以更轻松地进行故障排除)。
我一直在寻找一些东西,每当我发现有用的东西时,它就会以另一种编程语言结束,并且找不到C中的任何等效项。
在客户端和服务器代码中同时使用睡眠似乎可以解决此问题,但是我认为这不是一个好习惯。
代码是一团糟,所以我将包括我认为与之相关的内容,还将包括指向完整代码的链接。我确实打算改进它,但是在尝试解决这个问题时很沮丧,以至于我变得更糟。
客户端
printf("Please specify the filename: ");
fgets(msg,1000,stdin); // get message from std input
if(strcmp(msg,"\n")==0) {
printf("Wrong file name or format\n");
printf("Please specify the filename: ");
fgets(msg,1000,stdin); // get message from std input
}
while(strcmp(msg,"!stop\n")) {
msg[strlen(msg)-1]='\0';
write(sockfd,msg,strlen(msg));
FILE *fp = NULL;
char filecontent[1000];
bzero(filecontent,sizeof(filecontent));
while( (n = read(sockfd,filecontent,1000)) && strcmp(filecontent,"Over and out!")!=0 ) {
if(strcmp(filecontent,"No such file")!=0 && fp == NULL) {
fp = fopen(msg,"w");
}
printf("%s",filecontent);
if(fp !=NULL)
fprintf(fp, "%s",filecontent);
bzero(filecontent,sizeof(filecontent));
}
if(fp != NULL)
fclose(fp);
printf("\nPlease specify the filename: ");
fgets(msg,1000,stdin); // get message from std input
if(strcmp(msg,"\n")==0) {
printf("Wrong file name or format\n");
printf("Please specify the filename: ");
fgets(msg,1000,stdin); // get message from std input
}
}
服务器端
char date[50];
time_t ticks;
struct tm *tinfo;
time(&ticks);
tinfo=localtime(&ticks);
strcpy(date,asctime(tinfo));
printf("DATA: %s\n",date);
write(newsocketfd,date,sizeof(date));
while( (n = read(newsocketfd,msg,1000)) && strcmp(msg,"!stop\n")!=0) {
//printf("MSG: %s\n",msg);
if(n<0)
error("ERROR READING");
/////////READING FILE/////////////
char *filename = malloc(sizeof(msg)+1);
strcpy(filename,msg);
printf("'server filename:%s'\n",filename);
FILE *fp = fopen( filename,"r");
if(fp == NULL) {
printf("No such file found\n");
write(newsocketfd,"No such file",sizeof("No such file"));
}
while( fp!=NULL && fgets(msg,1000,fp)!=NULL){
write(newsocketfd,msg,sizeof(msg));
msg[strlen(msg)-1]='\0';
printf("server: '%s'\n",msg);
bzero(msg,sizeof(msg));
}
bzero(msg,sizeof(msg));
bzero(filename,strlen(filename));
n = write(newsocketfd,"Over and out!",sizeof("Over and out!"));
printf("Over\n");
}
很抱歉有任何头痛。 Full code here.
示例:
I think this pretty much shows the problem
我的想法是,服务器逐行读取文件,并将文件逐行发送给客户端,完成后,服务器发送“ over”消息,客户端停止从那里读取,但是似乎客户永远不会收到所有信息或“结束”信号。值得补充的是,如果我在本地计算机上运行这两个代码,这将非常正常。
答案 0 :(得分:0)
欢迎来到网络编程的世界!网络协议分层是有原因的。当您在TCP套接字上发送某些内容并立即关闭该套接字时,传递是不可靠的:它可能已正确传递给对等方,或者可能由于竞争条件而消失。
唯一可靠的方法是仅在对等方发送确认它可以接收所有已发送内容的确认时关闭套接字。标准协议为此使用了控制消息,您确实应该考虑这一点,但是如果不需要警告服务器发生客户端故障,则可以简单地让客户端在收到"Over and out!"
后关闭连接。顺便说一句,您应该意识到,由于TCP是一种流协议,因此无法保证该消息不会被拆分成一个以上的读取或连接到其他字节。因此,您应该保留上一个读取的末尾(信号字符串的大小减去一个字节),将下一个读取连接到该末尾,然后在缓冲区中的任何位置搜索该字符串。
另一种常见的方式是使用graceful shutdown:发送者使用shutdown(socket.SHUT_WR)
来表示通信已经通过而不关闭套接字并等待(带有{{1 }}),让对等方在正确交付所有内容后关闭套接字。