【发布时间】:2015-01-03 17:53:44
【问题描述】:
嗨,在使用套接字编程创建一个简单的 ftp 程序时,我遇到了以下问题。
简单介绍一下我的应用
服务器端:读取客户端请求的文件,然后将其写入客户端套接字。
客户端:读取客户端发送的数据并保存到磁盘。
当我从服务器传输普通文本文件时,我在客户端获得了正确的文件。但是,当我传输一些其他文件(如 pdf 或可执行文件)时,当我比较两个文件时,它们的大小相同,但我的客户端保存到磁盘的文件已损坏。
例如,如果我的服务器将 4000 字节的二进制文件写入客户端套接字。然后当我的客户将它保存到磁盘时,大小是相同的 4000 字节。但是,当我使用 chmod 授予它可执行权限并尝试执行它时,我收到如下错误:无法执行二进制文件。
类似地,当我传输 pdf 文件时,当我双击打开时,什么也没有显示。
在客户端,我还检查了 read 调用是否正在读取整个数据,并且它正在从套接字读取整个数据。
这与序列化有关吗?我的客户端和服务器都运行在同一个系统上,只用同一个编译器编译。
我的程序很大,有很多错误检查,所以我在这里粘贴了一些修改过的代码来解释这个问题为简单起见,我也使用了很多静态的东西:
server.c
int main(int argc, char* argv[])
{
// validate proper usage
if (argc != 4)
{
fprintf(stderr, "Usage %s <serverBindIP> <serverBindPort> <CredentialsFilePath>\n", argv[0]);
exit(-1);
}
// create signal hanlder's
// TODO
// store the command line arguments supplied
char* ip = argv[1];
int port = htons(atoi(argv[2]));
char* passwd_file = argv[3];
struct sockaddr_in server_addr, client_addr;
int server_fd, client_fd, result;
socklen_t length;
// Create an internet domain TCP socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1)
{
fprintf(stderr, "Unable to create socket\n");
exit(-1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = port;
server_addr.sin_addr.s_addr = inet_addr(ip);
// bind socket to an network interface
result = bind(server_fd, (struct sockaddr*) &server_addr, sizeof(server_addr));
if (result == -1)
{
fprintf(stderr, "Unable to bind socket\n");
exit(-1);
}
// mark the socket used for incoming requests
listen(server_fd, 5);
// accept an incoming connection
printf("Waiting for incoming connection\n");
length = sizeof(client_addr);
client_fd = accept(server_fd, (struct sockaddr*) &client_addr, &length);
if (client_fd == -1)
{
fprintf(stderr, "Unable to accept peer connection\n");
exit(-1);
}
// read and send one full file
struct stat stats;
stat("/home/xpansat/book.pdf", &stats);
int size = stats.st_size;
// send size of file to the client
write(client_fd, &size, sizeof(int));
FILE* in = fopen("/home/xpansat/book.pdf", "rb");
char *buffer = malloc(size);
fread(buffer, 1, size, in);
write(client_fd, buffer, size);
fclose(in);
返回 0; }
client.c
int main(int argc, char* argv[])
{
// validate proper usage
if (argc != 3)
{
fprintf(stderr, "Usage: %s <serverIP> <serverPort>\n", argv[0]);
exit(-1);
}
// store the command line arguments
char *server_ip = argv[1];
int server_port = htons(atoi(argv[2]));
// stores address of remote server to connect
struct sockaddr_in server_addr;
int fd, option;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
fprintf(stderr, "Error creating socket\n");
exit(-1);
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(server_ip);
server_addr.sin_port = server_port;
if (connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
fprintf(stderr, "Error connecting to server\n");
exit(-1);
}
int size = 0;
// read file size first
read(fd, &size, sizeof(int));
int bytes_read = 0;
int to_read = size;
FILE* out = fopen("book2.pdf", "wb");
char *buffer = malloc(size);
do
{
bytes_read = read(fd, buffer, to_read);
printf("To read: %d\n", to_read);
printf("Data read: %d\n", bytes_read);
to_read = to_read - bytes_read;
// save content to disk
fwrite(buffer, 1, bytes_read, out);
} while (to_read != 0);
return 0;
}
虽然我对这段代码有很好的建议,但我知道的这段代码粘贴在这里并没有真正说明我的问题,因为我发现在填充缓冲区以发送给客户端时我正在复制数据使用 strncpy 函数进入它,这会使可执行文件以某种方式损坏(可能是因为它放在最后的额外 \0 但我不知道为什么)。所以真正解决我的问题的事情是:用 memcpy 函数替换所有 strncpy 函数,现在我也能够正确传输二进制文件了。所以,这解决了我的问题。
【问题讨论】:
-
好吧,当传输文件时,服务器指示整个文件何时发送是很正常的(例如,通过关闭套接字以便客户端读取以 0 重新运行,或者使用一些更高的级协议)。至少可以说,在客户端获取一些文件大小是很糟糕的。
-
当您像这样编码时,更明确地说明数据的字节序通常是明智的。如果客户端和服务器不同意,你会得到一些令人讨厌的结果。
-
完成后客户端和服务器都应该关闭()套接字
-
我强烈建议使用 'send()' 将消息/文件发送到客户端,而不是使用 'write()'
-
我强烈建议使用'recv()' 从服务器获取消息/文件,而不是使用'read()'