这是针对linux系统的,C语言,涉及到网络编程。它用于文件传输程序。
我一直遇到这个问题,这段代码无法预测。它要么完全成功,要么客户端中的 while 循环永远不会结束。我发现这是因为 fileLength 变量有时会是一个巨大的(负或正)值,我认为这是由于使用 ntohl 时犯了一些错误。当我输入 print 语句时,它似乎运行完美,没有错误。
这是客户端代码:
//...here includes relevant header files
int main (int argc, char *argv[]) {
//socket file descriptor
int sockfd;
if (argc != 2) {
fprintf (stderr, "usage: client hostname\n");
exit(1);
}
//...creates socket file descriptor, connects to server
//create buffer for filename
char name[256];
//recieve filename into name buffer, bytes recieved stored in numbytes
if((numbytes = recv (sockfd, name, 255 * sizeof (char), 0)) == -1) {
perror ("recv");
exit(1);
}
//Null terminator after the filename
name[numbytes] = '\0';
//length of the file to recieve from server
long fl;
memset(&fl, 0, sizeof fl);
//recieve filelength from server
if((numbytes = recv (sockfd, &fl, sizeof(long), 0)) == -1) {
perror ("recv");
exit(1);
}
//convert filelength to host format
long fileLength = ntohl(fl);
//check to make sure file does not exist, so that the application will not overwrite exisitng files
if (fopen (name, "r") != NULL) {
fprintf (stderr, "file already present in client directory\n");
exit(1);
}
//open file called name in write mode
FILE *filefd = fopen (name, "wb");
//variable stating amount of data recieved
long bytesTransferred = 0;
//Until the file is recieved, keep recieving
while (bytesTransferred < fileLength) {
printf("transferred: %d\ntotal: %d\n", bytesTransferred, fileLength);
//set counter at beginning of unwritten segment
fseek(filefd, bytesTransferred, SEEK_SET);
//buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
char buf[256];
//recieve segment from server
if ((numbytes = recv (sockfd, buf, sizeof buf, 0)) == -1) {
perror ("recv");
exit(1);
}
//first byte of buffer, stating number of bytes of data in recieved segment
//converting from char to short requires adding 128, since the char ranges from -128 to 127
short bufLength = buf[0] + 128;
//write buffer into file, starting after the first byte of the buffer
fwrite (buf + 1, 1, bufLength * sizeof (char), filefd);
//add number of bytes of data recieved to bytesTransferred
bytesTransferred += bufLength;
}
fclose (filefd);
close (sockfd);
return 0;
}
这是服务器代码:
//...here includes relevant header files
int main (int argc, char *argv[]) {
if (argc != 2) {
fprintf (stderr, "usage: server filename\n");
exit(1);
}
//socket file descriptor, file descriptor for specific client connections
int sockfd, new_fd;
//...get socket file descriptor for sockfd, bind sockfd to predetermined port, listen for incoming connections
//...reaps zombie processes
printf("awaiting connections...\n");
while(1) {
//...accepts any incoming connections, gets file descriptor and assigns to new_fd
if (!fork()) {
//close socket file discriptor, only need file descriptor for specific client connection
close (sockfd);
//open a file for reading
FILE *filefd = fopen (argv[1], "rb");
//send filename to client
if (send (new_fd, argv[1], strlen (argv[1]) * sizeof(char), 0) == -1)
{ perror ("send"); }
//put counter at end of selected file, and find length
fseek (filefd, 0, SEEK_END);
long fileLength = ftell (filefd);
//convert length to network form and send it to client
long fl = htonl(fileLength);
//Are we sure this is sending all the bytes??? TEST
if (send (new_fd, &fl, sizeof fl, 0) == -1)
{ perror ("send"); }
//variable stating amount of data unsent
long len = fileLength;
//Until file is sent, keep sending
while(len > 0) {
printf("remaining: %d\ntotal: %d\n", len, fileLength);
//set counter at beginning of unread segment
fseek (filefd, fileLength - len, SEEK_SET);
//length of the segment; 255 unless last segment
short bufLength;
if (len > 255) {
len -= 255;
bufLength = 255;
} else {
bufLength = len;
len = 0;
}
//buffer of 256 bytes; 1 byte for byte-length of segment, 255 bytes of data
char buf[256];
//Set first byte of buffer as the length of the segment
//converting short to char requires subtracting 128
buf[0] = bufLength - 128;
//read file into the buffer starting after the first byte of the buffer
fread(buf + 1, 1, bufLength * sizeof(char), filefd);
//Send data too client
if (send (new_fd, buf, sizeof buf, 0) == -1)
{ perror ("send"); }
}
fclose (filefd);
close (new_fd);
exit (0);
}
close (new_fd);
}
return 0;
}
注意:我已经稍微简化了代码,希望它更清晰。 以//... 开头的任何内容都代表一堆代码
最佳答案
您似乎假设每个 send()
要么传输指定的全部字节数,要么出错,并且每个都将与 recv() 完美配对
在另一边,使得 recv()
接收到由 send()
发送的字节数(或错误输出),仅此而已不少于。这些都不是安全的假设。
您没有显示用于设置网络连接的代码。如果您使用的是基于数据报的协议(protocol)(即 UDP),那么您更有可能获得预期的发送/接收边界匹配,但您需要考虑数据包丢失或损坏的可能性。如果您使用的是基于流的协议(protocol)(即 TCP),那么您不必太担心数据丢失或损坏,但您根本没有理由期望边界匹配行为。
你至少需要三样东西:
网络层之上的应用层协议(protocol)。您已经掌握了其中的一部分,例如您如何首先传输文件长度以告知客户预期的内容,但您需要对所有传输的非预定、固定长度的数据执行类似的操作。或者,发明另一种方式来传达数据边界。
每个旨在传输一个以上字节的
send()
/write()
都必须在一个循环中执行,以适应被分成多个部分的传输.返回值告诉您有多少请求的字节已传输(或至少有多少已移交给网络堆栈),如果少于请求的字节数,您必须循环返回以尝试传输其余字节。旨在传输一个以上字节的每个
recv()
/read()
都必须在一个循环中执行,以适应被分成多个部分的传输.我建议按照send()
中描述的相同行构建它,但您也可以选择接收数据,直到看到预先安排的分隔符。然而,基于定界符的方法更为复杂,因为它需要在接收端进行额外的缓冲。
如果没有这些措施,您的服务器和客户端很容易失去同步。其中可能的结果是客户端将部分文件名或部分文件内容解释为文件长度。
关于c - Printf 使程序在 C 中工作,htonl 和 ntohl 不工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39725968/