未从 TCP 连接接收文件传输
Not receiving File transfer from TCP connection
我正在为学校项目编写 TCP 客户端和服务器协议。客户端发送 "GET \r\n" 消息,服务器必须传输“+OK\r\n”、文件大小和文件,以防它存在于服务器目录中。我在文件传输中被阻止
我试图一次一小步地解决它。我建立了连接,从客户端发送请求并从服务器接收到 "OK" 消息。
现在我在服务器中打开文件并尝试将它一次发送 128 个字节到客户端。文件的读取有效,显然也发送了缓冲区,但客户端没有收到任何东西...
这是我的 server.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* Buffer length */
#define TIMEOUT 15 /* TIMEOUT */
/* FUNCTION PROTOTYPES */
void service(int s);
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
int conn_request_skt; /* passive socket */
uint16_t lport_n, lport_h; /* port used by server (net/host ord.) */
int bklog = 2; /* listen backlog */
int s; /* connected socket */
fd_set cset; // waiting for connection
struct timeval tval; // timeout
size_t n;
socklen_t addrlen;
struct sockaddr_in saddr, caddr; /* server and client addresses */
prog_name = argv[0];
if (argc != 2) {
printf("Usage: %s <port number>\n", prog_name);
exit(1);
}
/* get server port number */
if (sscanf(argv[1], "%" SCNu16, &lport_h)!=1)
err_sys("Invalid port number");
lport_n = htons(lport_h);
/* create the socket */
printf("creating socket...\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done, socket number %u\n",s);
/* bind the socket to any local IP address */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address", &saddr);
Bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
/* listen */
printf ("Listening at socket %d with backlog = %d \n",s,bklog);
Listen(s, bklog);
printf("done.\n");
conn_request_skt = s;
/* main server loop */
for ( ; ; )
{
printf("waiting for connection...\n");
/* accept next connection */
FD_ZERO(&cset);
FD_SET(conn_request_skt, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
n = Select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if ( n > 0 ){
addrlen = sizeof(struct sockaddr_in);
s = Accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s);
/* serve the client on socket s */
service(s);
} else {
printf("No connection request after %d seconds\n",TIMEOUT);
}
}
}
void service(int s) {
char buf[BUFLEN]; /* reception buffer */
char filename[BUFLEN];
int n;
long filesize;
uint32_t fsize;
FILE *fp;
for ( ; ; )
{
n = recv(s, buf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received request from socket %03d :\n", s);
sscanf(buf, "GET %s\r\n", filename);
strcpy(buf, "+OK\r\n");
printf("%s",buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while sending +OK\n");
// open file
fp = fopen(filename, "r");
if( fp == NULL){
//TODO close connection
}
// calculating dim of file
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
rewind(fp); // go back at beginning of file
fsize = htonl(filesize); // size file in network byte order
// sending file size
if(writen(s, &fsize, 4) != 4)
printf("Write error while sending file size\n");
while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
printf("%s", buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while buf\n");
}
printf("%s", buf);
printf("I am here\n");
}
}
}
虽然这是我的 client.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* BUFFER LENGTH */
#define TIMEOUT 15 /* TIMEOUT*/
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
char request[BUFLEN]; /* request buffer */
char rbuf[BUFLEN]; /* reception buffer */
uint32_t taddr_n; /* server IP addr. (net/host ord) */
uint16_t tport_n, tport_h; /* server port number (net/host ord) */
int s, len;
int result;
struct sockaddr_in saddr; /* server address structure */
struct in_addr sIPaddr; /* server IP addr. structure */
fd_set cset; // variables for timeout
struct timeval tval;
size_t n;
prog_name = argv[0];
if(argc < 4)
err_sys("Wrong number of parameters!\n");
// read address from first argument
taddr_n = inet_addr(argv[1]);
if (taddr_n == INADDR_NONE)
err_sys("Invalid address");
// read port number from second argument
if (sscanf(argv[2], "%" SCNu16, &tport_h)!=1)
err_sys("Invalid port number");
tport_n = htons(tport_h);
/* create the socket */
printf("Creating socket\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done. Socket fd number: %d\n",s);
/* prepare address structure */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = tport_n;
saddr.sin_addr = sIPaddr;
printf("trying to connect to the server...\n");
/* connect */
showAddr("Connecting to target address", &saddr);
Connect(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
// loop to request files
for (int i = 3 ; i < argc ; i++ ){ // i = 4 because the first file is the fourth argument
// check if file name is too big
if(strlen(argv[i]) >= BUFLEN - 6)
err_sys("The file name is too big for the buffer request!\n");
// create the string of bytes for the request
strcpy(request, "GET ");
strcat(request, argv[i]);
strcat(request, "\r\n");
len = strlen(request);
if(writen(s, request, len) != len){
printf("Write error\n");
break;
}
printf("waiting for response...\n");
// receive file from server
n = recv(s, rbuf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received reply from server\n");
uint32_t fsize;
printf("%s",rbuf);
if(strcmp(rbuf, "+OK\r\n") == 0){
n = recv(s, &fsize, 4, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
// received file dimension
fsize = ntohl(fsize);
}
while(fsize > 0){
printf("I am here1n\n");
// receive file
n = recv(s, rbuf, BUFLEN-1, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("I am here");
fsize -= n;
}
}
}
}
}
printf("===========================================================\n");
close(s);
exit(0);
}
我应该接收文件的客户端中的 recv 只是阻塞而没有接收到任何东西。我不明白我错过了什么...
这里的问题很常见:您没有注意消息边界。
在你的客户端中,你做了一个recv
并检查字节数是否大于0。但是你没有做更多的长度检查。您接下来对您期望接收的特定字符串执行 strcmp
(+OK\r\n
)。但是您可能收到了 3 个字节 (+OK
) 或者您可能收到了 10: (+OK\r\nXXXXX
) 或更多 [另外:另外,recv
不能保证您的字节字符串为 null-终止]。没有什么可以阻止远端的内核将前导码 plus 后续字节批处理到单个 TCP 数据包中。同样,没有什么可以阻止本地端将多个 TCP 数据包聚合到一个缓冲区中。
您 必须提供消息边界。如果您期望您的下一条消息是 5 个字节,那么您应该正好收到 5 个字节(如果收到的字节更少,则重试——也要小心检查 EOF,以防对方提前中止)。或者,或者,在您的接收逻辑前面粘贴一个缓冲层,以便它可以接收大量的 return 给您您想要的字节数,然后保存多余的部分以供后续 "receive"打电话。
换句话说:您的服务器发送 +OK\r\n
,然后发送一个四字节长度的文件,然后开始发送文件。但这意味着您在客户端的第一个 recv
可以 在一次系统调用中接收序言、长度以及文件的前 N 个字节。
TCP 不遵守、不提供或强制消息边界。
我正在为学校项目编写 TCP 客户端和服务器协议。客户端发送 "GET \r\n" 消息,服务器必须传输“+OK\r\n”、文件大小和文件,以防它存在于服务器目录中。我在文件传输中被阻止
我试图一次一小步地解决它。我建立了连接,从客户端发送请求并从服务器接收到 "OK" 消息。
现在我在服务器中打开文件并尝试将它一次发送 128 个字节到客户端。文件的读取有效,显然也发送了缓冲区,但客户端没有收到任何东西...
这是我的 server.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* Buffer length */
#define TIMEOUT 15 /* TIMEOUT */
/* FUNCTION PROTOTYPES */
void service(int s);
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
int conn_request_skt; /* passive socket */
uint16_t lport_n, lport_h; /* port used by server (net/host ord.) */
int bklog = 2; /* listen backlog */
int s; /* connected socket */
fd_set cset; // waiting for connection
struct timeval tval; // timeout
size_t n;
socklen_t addrlen;
struct sockaddr_in saddr, caddr; /* server and client addresses */
prog_name = argv[0];
if (argc != 2) {
printf("Usage: %s <port number>\n", prog_name);
exit(1);
}
/* get server port number */
if (sscanf(argv[1], "%" SCNu16, &lport_h)!=1)
err_sys("Invalid port number");
lport_n = htons(lport_h);
/* create the socket */
printf("creating socket...\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done, socket number %u\n",s);
/* bind the socket to any local IP address */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address", &saddr);
Bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
/* listen */
printf ("Listening at socket %d with backlog = %d \n",s,bklog);
Listen(s, bklog);
printf("done.\n");
conn_request_skt = s;
/* main server loop */
for ( ; ; )
{
printf("waiting for connection...\n");
/* accept next connection */
FD_ZERO(&cset);
FD_SET(conn_request_skt, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
n = Select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if ( n > 0 ){
addrlen = sizeof(struct sockaddr_in);
s = Accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s);
/* serve the client on socket s */
service(s);
} else {
printf("No connection request after %d seconds\n",TIMEOUT);
}
}
}
void service(int s) {
char buf[BUFLEN]; /* reception buffer */
char filename[BUFLEN];
int n;
long filesize;
uint32_t fsize;
FILE *fp;
for ( ; ; )
{
n = recv(s, buf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received request from socket %03d :\n", s);
sscanf(buf, "GET %s\r\n", filename);
strcpy(buf, "+OK\r\n");
printf("%s",buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while sending +OK\n");
// open file
fp = fopen(filename, "r");
if( fp == NULL){
//TODO close connection
}
// calculating dim of file
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
rewind(fp); // go back at beginning of file
fsize = htonl(filesize); // size file in network byte order
// sending file size
if(writen(s, &fsize, 4) != 4)
printf("Write error while sending file size\n");
while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
printf("%s", buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while buf\n");
}
printf("%s", buf);
printf("I am here\n");
}
}
}
虽然这是我的 client.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* BUFFER LENGTH */
#define TIMEOUT 15 /* TIMEOUT*/
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
char request[BUFLEN]; /* request buffer */
char rbuf[BUFLEN]; /* reception buffer */
uint32_t taddr_n; /* server IP addr. (net/host ord) */
uint16_t tport_n, tport_h; /* server port number (net/host ord) */
int s, len;
int result;
struct sockaddr_in saddr; /* server address structure */
struct in_addr sIPaddr; /* server IP addr. structure */
fd_set cset; // variables for timeout
struct timeval tval;
size_t n;
prog_name = argv[0];
if(argc < 4)
err_sys("Wrong number of parameters!\n");
// read address from first argument
taddr_n = inet_addr(argv[1]);
if (taddr_n == INADDR_NONE)
err_sys("Invalid address");
// read port number from second argument
if (sscanf(argv[2], "%" SCNu16, &tport_h)!=1)
err_sys("Invalid port number");
tport_n = htons(tport_h);
/* create the socket */
printf("Creating socket\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done. Socket fd number: %d\n",s);
/* prepare address structure */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = tport_n;
saddr.sin_addr = sIPaddr;
printf("trying to connect to the server...\n");
/* connect */
showAddr("Connecting to target address", &saddr);
Connect(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
// loop to request files
for (int i = 3 ; i < argc ; i++ ){ // i = 4 because the first file is the fourth argument
// check if file name is too big
if(strlen(argv[i]) >= BUFLEN - 6)
err_sys("The file name is too big for the buffer request!\n");
// create the string of bytes for the request
strcpy(request, "GET ");
strcat(request, argv[i]);
strcat(request, "\r\n");
len = strlen(request);
if(writen(s, request, len) != len){
printf("Write error\n");
break;
}
printf("waiting for response...\n");
// receive file from server
n = recv(s, rbuf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received reply from server\n");
uint32_t fsize;
printf("%s",rbuf);
if(strcmp(rbuf, "+OK\r\n") == 0){
n = recv(s, &fsize, 4, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
// received file dimension
fsize = ntohl(fsize);
}
while(fsize > 0){
printf("I am here1n\n");
// receive file
n = recv(s, rbuf, BUFLEN-1, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("I am here");
fsize -= n;
}
}
}
}
}
printf("===========================================================\n");
close(s);
exit(0);
}
我应该接收文件的客户端中的 recv 只是阻塞而没有接收到任何东西。我不明白我错过了什么...
这里的问题很常见:您没有注意消息边界。
在你的客户端中,你做了一个recv
并检查字节数是否大于0。但是你没有做更多的长度检查。您接下来对您期望接收的特定字符串执行 strcmp
(+OK\r\n
)。但是您可能收到了 3 个字节 (+OK
) 或者您可能收到了 10: (+OK\r\nXXXXX
) 或更多 [另外:另外,recv
不能保证您的字节字符串为 null-终止]。没有什么可以阻止远端的内核将前导码 plus 后续字节批处理到单个 TCP 数据包中。同样,没有什么可以阻止本地端将多个 TCP 数据包聚合到一个缓冲区中。
您 必须提供消息边界。如果您期望您的下一条消息是 5 个字节,那么您应该正好收到 5 个字节(如果收到的字节更少,则重试——也要小心检查 EOF,以防对方提前中止)。或者,或者,在您的接收逻辑前面粘贴一个缓冲层,以便它可以接收大量的 return 给您您想要的字节数,然后保存多余的部分以供后续 "receive"打电话。
换句话说:您的服务器发送 +OK\r\n
,然后发送一个四字节长度的文件,然后开始发送文件。但这意味着您在客户端的第一个 recv
可以 在一次系统调用中接收序言、长度以及文件的前 N 个字节。
TCP 不遵守、不提供或强制消息边界。