i/o 通过 TCP 套接字调用的问题
Issues with i/o calls over TCP socket
对于某些背景,我正在编写一个 TCP 套接字服务器/客户端通信程序来将一个文本文件复制到另一个文本文件。我正在努力实现通过套接字读取。我正在 linux.
上开发
这是服务端读取函数,当客户端读取函数通过套接字发送读取操作码时调用。程序使用正确的 fd 和计数正确地到达此代码。
/* s_read
* server side read
* Send in socket: 4 bytes return value, 4 bytes errno, n bytes data
*/
int s_read(int conn){
// Get fd
unsigned char fdstr[4];
int L = sizeof(char) * 4;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = read(conn, fdstr + total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive fd\n");
return -1;
}
total += bytes;
}
int fd = 0;
fd = (fdstr[0] << 24) | (fdstr[1] << 16) | (fdstr[2] << 8) | fdstr[3];
// Get count
unsigned char countstr[4];
L = sizeof(char) * 4;
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = read(conn, countstr + total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive count\n");
return -1;
}
total += bytes;
}
int count = 0;
count = (countstr[0] << 24) | (countstr[1] << 16) | (countstr[2] << 8) | countstr[3];
// Prepare return message
int return_value;
L = 8 + // 4 bytes for return value, 4 bytes for errno
count; // count bytes for read in data
int error = 0;
char * msg;
char * read_value = malloc(count);
// Execute open call
bytes = 0;
total = 0;
while ( total < count ) {
if ( (return_value = read(fd, read_value + total, count - total)) < 0 ) {
error = errno;
break;
}
total += return_value;
}
// Build return message
msg = malloc(L);
L=0;
msg[L++] = (return_value >> 24) & 0xff; // put the kernel return value
msg[L++] = (return_value >> 16) & 0xff;
msg[L++] = (return_value >> 8) & 0xff;
msg[L++] = (return_value ) & 0xff;
msg[L++] = (error >> 24) & 0xff; // put the errno
msg[L++] = (error >> 16) & 0xff;
msg[L++] = (error >> 8) & 0xff;
msg[L++] = (error ) & 0xff;
for (int i=0; i < count; i++)
msg[L++] = read_value[i]; // put the read in data.
// Send return message
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = write(conn, msg + total, L - total)) < 0) {
printf("r_server.c::s_read: Error sending r_read return value to client\n");
return -1;
}
total += bytes;
}
free(read_value);
free(msg);
return 0;
}
这是客户端读取函数,它将有效负载放在一起并通过套接字发送,以请求服务器端读取。
/* r_read
* remote read
*/
int r_read(int fd, void *buf, int count) {
int L;
char * msg;
int in_msg;
int in_err;
L = 1 + // byte for opcode
sizeof(fd) + // int bytes for fd.
sizeof(count); // int bytes for count.
msg = malloc(L);
L=0;
msg[L++] = 3; // this is the code for read.
msg[L++] = (fd >> 24) & 0xff; // put the fd.
msg[L++] = (fd >> 16) & 0xff;
msg[L++] = (fd >> 8) & 0xff;
msg[L++] = (fd ) & 0xff;
msg[L++] = (count >> 24) & 0xff; // put the count.
msg[L++] = (count >> 16) & 0xff;
msg[L++] = (count >> 8) & 0xff;
msg[L++] = (count ) & 0xff;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i++) {
*(char *)(buf + i) = msg[i + 8];
}
errno = in_err;
free(msg);
return in_msg;
}
客户端在 in_msg 中接收到伪造的 return_value,但服务器在发送之前看到了正确的 return_value。两端实际读入的数据貌似也是假的
这是实际调用 i/o 函数将一个文件复制到另一个文件的代码。我有一个类似的文件,可以从 local 文件复制到远程文件,并且可以正常工作。在 main 接收到套接字信息并从 argv[].
中剥离这些 args 后从 main 调用它
int entry(int argc, char* argv[]){
// Input guards
if (argc != 3) {
printf("Invalid arguments\n");
return -1;
}
// Get file names
char *filename = argv[1];
char *copyfilename = argv[2];
printf("rclient2.c::entry: Copying remote file %s to local file %s\n", filename, copyfilename);
// Open files
int fd = r_open((const char*) filename, O_RDWR, (mode_t) 0600);
if (fd < 0) {
printf("rclient2.c::entry: r_open failed.\n");
return -1;
}
int copyfd = open((const char*) copyfilename, O_RDWR | O_CREAT, (mode_t) 0600);
if (copyfd < 0) {
printf("rclient2.c::entry: open failed.\n");
return -1;
}
// Seek to position 10
//r_lseek(fd, 10, SEEK_SET); // Later requirement once read is working
// Copy file
char buf;
while ( r_read(fd, &buf, 1) > 0 ) {
if (write(copyfd, &buf, 1) < 0) {
printf("rclient2::entry: write failed.\n");
return -1;
}
}
// Close files
r_close(fd);
close(copyfd);
return 0;
}
有关错误行为的更多详细信息:
服务器在关闭前只收到 6 次 s_read() 调用,本应收到大约 41 次调用。在读取“复制”文件时,它充满了人类无法阅读的虚假值。 None 我的错误被触发,除了 close() 调用之后的一个错误。特别是“r_server.c::main: 无法接收操作码。”错误。因此,我在下面包含了服务器的主要功能(它读取客户端功能提供的操作码)。
/* main - server implementation
*/
int main(int argc, char *argv[]){
// Setup socket data
int listener, conn;
unsigned int length;
struct sockaddr_in s1, s2;
// Create server socket
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0) {
printf("r_server.c::main: Error creating server socket\n");
return -1;
}
// Establish server socket
bzero((char *) &s1, sizeof(s1));
s1.sin_family = AF_INET;
s1.sin_port = htons(0);
s1.sin_addr.s_addr = inet_addr("127.0.0.1");
if( bind( listener, (struct sockaddr *) &s1, sizeof(s1)) < 0) {
printf("r_server.c::main: Server couldn't bind to the port\n");
return -1;
}
// Print port and start server
length = sizeof(s1);
getsockname(listener, (struct sockaddr *) &s1, &length);
printf("%d\n", s1.sin_port);
if ( listen(listener, 1) < 0) {
printf("r_server.c::main: Server error while listening\n");
return -1;
}
// While server running
while(1) {
// Connect to new client
length = sizeof(s2);
conn = accept(listener, (struct sockaddr *) &s2, &length);
if( conn < 0 ) {
printf("r_server.c::main: Server failed to accept incoming stuff\n");
return -1;
}
// Fork to manage client and listen for new clients
if (fork() == 0) {
// Until client disconnects
while (1) {
// Get opcode
unsigned char opcode;
int success;
if ((success = read(conn, &opcode, sizeof(opcode))) < 0) {
printf("r_server.c::main: Couldn't receive opcode. opcode = %d\n", opcode);
return -1;
}
// Client disconnected
if (success == 0) {
return 0;
}
// Call related server side function
switch (opcode) {
case (1):
printf("Opening...\n");
if (s_open(conn) < 0)
return -1;
break;
case (2):
printf("Closing...\n");
if (s_close(conn) < 0)
return -1;
break;
case (3):
printf("Reading...\n");
if (s_read(conn) < 0)
return -1;
break;
case (4):
printf("Writing...\n");
if (s_write(conn) < 0)
return -1;
break;
case (5):
printf("Seeking...\n");
if (s_lseek(conn) < 0)
return -1;
break;
case (6):
printf("Piping...\n");
if (s_pipe(conn) < 0)
return -1;
break;
case (7):
printf("Duping...\n");
if (s_dup2(conn) < 0)
return -1;
break;
default:
return -1;
}
}
return 0;
}
}
return 0;
}
代码盲目地假设 read
将始终准确读取请求的字节数,而 write
将始终写入给定的完整缓冲区。这两个假设都是错误的。
read
只会读取 最多 给定的大小,但实际上可能会读取更少。类似的 write
只会写入 至多 整个缓冲区,但可能会写入更少。因此必须检查 return 值以确定实际读取或写入了多少数据。如果没有读取或写入所有内容,则必须对剩余数据重复操作,甚至可能重复多次。
在 r_read() 函数中,我们从未读入服务器放入响应中的值。我们只读取对应于 return_value 和 errno.
的 8 个字节
而不是
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i++) {
*(char *)(buf + i) = msg[i + 8];
}
我们需要做的
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3]; // # of bytes remaining in the socket buffer (the data we asked for)
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
bytes = 0;
total = 0;
while ( total < in_msg ) {
if ( (bytes = read(sock, buf + total, in_msg - total)) < 0) {
printf("Failed to read the r_read data from server\n");
return -1;
}
}
请注意,仅当您需要处理读取或写入调用未完全 成功的情况时,才需要使用 while ( total ...)
逻辑。就我而言,如果发生这种情况,我们可以简单地退出并返回错误。
对于某些背景,我正在编写一个 TCP 套接字服务器/客户端通信程序来将一个文本文件复制到另一个文本文件。我正在努力实现通过套接字读取。我正在 linux.
上开发这是服务端读取函数,当客户端读取函数通过套接字发送读取操作码时调用。程序使用正确的 fd 和计数正确地到达此代码。
/* s_read
* server side read
* Send in socket: 4 bytes return value, 4 bytes errno, n bytes data
*/
int s_read(int conn){
// Get fd
unsigned char fdstr[4];
int L = sizeof(char) * 4;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = read(conn, fdstr + total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive fd\n");
return -1;
}
total += bytes;
}
int fd = 0;
fd = (fdstr[0] << 24) | (fdstr[1] << 16) | (fdstr[2] << 8) | fdstr[3];
// Get count
unsigned char countstr[4];
L = sizeof(char) * 4;
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = read(conn, countstr + total, L - total)) < 0) {
printf("r_server.c::s_read: Couldn't receive count\n");
return -1;
}
total += bytes;
}
int count = 0;
count = (countstr[0] << 24) | (countstr[1] << 16) | (countstr[2] << 8) | countstr[3];
// Prepare return message
int return_value;
L = 8 + // 4 bytes for return value, 4 bytes for errno
count; // count bytes for read in data
int error = 0;
char * msg;
char * read_value = malloc(count);
// Execute open call
bytes = 0;
total = 0;
while ( total < count ) {
if ( (return_value = read(fd, read_value + total, count - total)) < 0 ) {
error = errno;
break;
}
total += return_value;
}
// Build return message
msg = malloc(L);
L=0;
msg[L++] = (return_value >> 24) & 0xff; // put the kernel return value
msg[L++] = (return_value >> 16) & 0xff;
msg[L++] = (return_value >> 8) & 0xff;
msg[L++] = (return_value ) & 0xff;
msg[L++] = (error >> 24) & 0xff; // put the errno
msg[L++] = (error >> 16) & 0xff;
msg[L++] = (error >> 8) & 0xff;
msg[L++] = (error ) & 0xff;
for (int i=0; i < count; i++)
msg[L++] = read_value[i]; // put the read in data.
// Send return message
bytes = 0;
total = 0;
while ( total < L ) {
if ( (bytes = write(conn, msg + total, L - total)) < 0) {
printf("r_server.c::s_read: Error sending r_read return value to client\n");
return -1;
}
total += bytes;
}
free(read_value);
free(msg);
return 0;
}
这是客户端读取函数,它将有效负载放在一起并通过套接字发送,以请求服务器端读取。
/* r_read
* remote read
*/
int r_read(int fd, void *buf, int count) {
int L;
char * msg;
int in_msg;
int in_err;
L = 1 + // byte for opcode
sizeof(fd) + // int bytes for fd.
sizeof(count); // int bytes for count.
msg = malloc(L);
L=0;
msg[L++] = 3; // this is the code for read.
msg[L++] = (fd >> 24) & 0xff; // put the fd.
msg[L++] = (fd >> 16) & 0xff;
msg[L++] = (fd >> 8) & 0xff;
msg[L++] = (fd ) & 0xff;
msg[L++] = (count >> 24) & 0xff; // put the count.
msg[L++] = (count >> 16) & 0xff;
msg[L++] = (count >> 8) & 0xff;
msg[L++] = (count ) & 0xff;
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i++) {
*(char *)(buf + i) = msg[i + 8];
}
errno = in_err;
free(msg);
return in_msg;
}
客户端在 in_msg 中接收到伪造的 return_value,但服务器在发送之前看到了正确的 return_value。两端实际读入的数据貌似也是假的
这是实际调用 i/o 函数将一个文件复制到另一个文件的代码。我有一个类似的文件,可以从 local 文件复制到远程文件,并且可以正常工作。在 main 接收到套接字信息并从 argv[].
中剥离这些 args 后从 main 调用它int entry(int argc, char* argv[]){
// Input guards
if (argc != 3) {
printf("Invalid arguments\n");
return -1;
}
// Get file names
char *filename = argv[1];
char *copyfilename = argv[2];
printf("rclient2.c::entry: Copying remote file %s to local file %s\n", filename, copyfilename);
// Open files
int fd = r_open((const char*) filename, O_RDWR, (mode_t) 0600);
if (fd < 0) {
printf("rclient2.c::entry: r_open failed.\n");
return -1;
}
int copyfd = open((const char*) copyfilename, O_RDWR | O_CREAT, (mode_t) 0600);
if (copyfd < 0) {
printf("rclient2.c::entry: open failed.\n");
return -1;
}
// Seek to position 10
//r_lseek(fd, 10, SEEK_SET); // Later requirement once read is working
// Copy file
char buf;
while ( r_read(fd, &buf, 1) > 0 ) {
if (write(copyfd, &buf, 1) < 0) {
printf("rclient2::entry: write failed.\n");
return -1;
}
}
// Close files
r_close(fd);
close(copyfd);
return 0;
}
有关错误行为的更多详细信息: 服务器在关闭前只收到 6 次 s_read() 调用,本应收到大约 41 次调用。在读取“复制”文件时,它充满了人类无法阅读的虚假值。 None 我的错误被触发,除了 close() 调用之后的一个错误。特别是“r_server.c::main: 无法接收操作码。”错误。因此,我在下面包含了服务器的主要功能(它读取客户端功能提供的操作码)。
/* main - server implementation
*/
int main(int argc, char *argv[]){
// Setup socket data
int listener, conn;
unsigned int length;
struct sockaddr_in s1, s2;
// Create server socket
listener = socket(AF_INET, SOCK_STREAM, 0);
if (listener < 0) {
printf("r_server.c::main: Error creating server socket\n");
return -1;
}
// Establish server socket
bzero((char *) &s1, sizeof(s1));
s1.sin_family = AF_INET;
s1.sin_port = htons(0);
s1.sin_addr.s_addr = inet_addr("127.0.0.1");
if( bind( listener, (struct sockaddr *) &s1, sizeof(s1)) < 0) {
printf("r_server.c::main: Server couldn't bind to the port\n");
return -1;
}
// Print port and start server
length = sizeof(s1);
getsockname(listener, (struct sockaddr *) &s1, &length);
printf("%d\n", s1.sin_port);
if ( listen(listener, 1) < 0) {
printf("r_server.c::main: Server error while listening\n");
return -1;
}
// While server running
while(1) {
// Connect to new client
length = sizeof(s2);
conn = accept(listener, (struct sockaddr *) &s2, &length);
if( conn < 0 ) {
printf("r_server.c::main: Server failed to accept incoming stuff\n");
return -1;
}
// Fork to manage client and listen for new clients
if (fork() == 0) {
// Until client disconnects
while (1) {
// Get opcode
unsigned char opcode;
int success;
if ((success = read(conn, &opcode, sizeof(opcode))) < 0) {
printf("r_server.c::main: Couldn't receive opcode. opcode = %d\n", opcode);
return -1;
}
// Client disconnected
if (success == 0) {
return 0;
}
// Call related server side function
switch (opcode) {
case (1):
printf("Opening...\n");
if (s_open(conn) < 0)
return -1;
break;
case (2):
printf("Closing...\n");
if (s_close(conn) < 0)
return -1;
break;
case (3):
printf("Reading...\n");
if (s_read(conn) < 0)
return -1;
break;
case (4):
printf("Writing...\n");
if (s_write(conn) < 0)
return -1;
break;
case (5):
printf("Seeking...\n");
if (s_lseek(conn) < 0)
return -1;
break;
case (6):
printf("Piping...\n");
if (s_pipe(conn) < 0)
return -1;
break;
case (7):
printf("Duping...\n");
if (s_dup2(conn) < 0)
return -1;
break;
default:
return -1;
}
}
return 0;
}
}
return 0;
}
代码盲目地假设 read
将始终准确读取请求的字节数,而 write
将始终写入给定的完整缓冲区。这两个假设都是错误的。
read
只会读取 最多 给定的大小,但实际上可能会读取更少。类似的 write
只会写入 至多 整个缓冲区,但可能会写入更少。因此必须检查 return 值以确定实际读取或写入了多少数据。如果没有读取或写入所有内容,则必须对剩余数据重复操作,甚至可能重复多次。
在 r_read() 函数中,我们从未读入服务器放入响应中的值。我们只读取对应于 return_value 和 errno.
的 8 个字节而不是
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3];
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
for (int i = 0; i < count; i++) {
*(char *)(buf + i) = msg[i + 8];
}
我们需要做的
int bytes = 0;
int total = 0;
while ( total < L ) {
if ( (bytes = write(sock, msg + total, L - total)) < 0) {
printf("Failed to send r_read to server\n");
return -1;
}
total += bytes;
}
bytes = 0;
total = 0;
while ( total < 8 ) {
if ( (bytes = read(sock, msg + total, 8 - total)) < 0) {
printf("Failed to receive r_read from server\n");
return -1;
}
total += bytes;
}
in_msg = (msg[0] << 24) | (msg[1] << 16) | (msg[2] << 8) | msg[3]; // # of bytes remaining in the socket buffer (the data we asked for)
in_err = (msg[4] << 24) | (msg[5] << 16) | (msg[6] << 8) | msg[7];
bytes = 0;
total = 0;
while ( total < in_msg ) {
if ( (bytes = read(sock, buf + total, in_msg - total)) < 0) {
printf("Failed to read the r_read data from server\n");
return -1;
}
}
请注意,仅当您需要处理读取或写入调用未完全 成功的情况时,才需要使用 while ( total ...)
逻辑。就我而言,如果发生这种情况,我们可以简单地退出并返回错误。