我试图使用套接字在C中编写服务器,这些套接字将接收来自多个客户端的命令。我试图理解的是:如果客户端发送包含4个字符的命令,而recv()函数被告知要接收5个字节的数据,会发生什么?
这是我的代码:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <time.h>
#include <dirent.h>
void *connection_handler(void *);
int sendall(int s, char *buf, int len, int flags);
int recvall(int s, char *buf, int len, int flags);
int main (int argc , char *argv[])
{
char check = 'n', *message;
int socket_d, port, socket_n, *sock_n, c;
struct sockaddr_in server_addr, client_addr;
socket_d = socket(AF_INET , SOCK_STREAM , 0);
if (socket_d == -1)
{
printf("Could not create socket. Exiting...\n");
return 1;
}
printf("Welcome to Random server v0.0.1.\n");
printf("Please input port (default - 3425): ");
scanf("%i", &port);
if (port >= 65536 || port < 0) port = 3425;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(port);
if( bind(socket_d,(struct sockaddr *)&server_addr , sizeof(server_addr)) < 0)
{
printf("Binding socket failed.\n");
return 1;
}
printf("Binding socket done.\n");
listen(socket_d, 3);
printf("Waiting for connections...\n");
c = sizeof(struct sockaddr_in);
while( (socket_n = accept(socket_d, (struct sockaddr *)&client_addr, (socklen_t*)&c)) )
{
puts("Connection accepted");
message = "Hello Client, I have received your connection. And now I will assign a handler for you\n";
send(socket_n , message , strlen(message), 0);
pthread_t sniffer_thread;
sock_n = malloc(1);
*sock_n = socket_n;
if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) sock_n) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( sniffer_thread , NULL);
puts("Handler assigned");
}
if (socket_n<0)
{
printf("Accept failed.\n");
return 1;
}
return 0;
}
void *connection_handler(void *socket_d)
{
printf("Thread created\n");
int sock = *(int*)socket_d;
int read_size;
char *message , client_message[200];
while (1)
{
message = malloc(5);
read_size = recv(sock , message , 5 , 0);
strcpy(client_message, message);
//send(sock , message , strlen(message), 0);
free(message);
if (strcmp(client_message, "list") == 0)
{
DIR * dir;
struct dirent * de;
strcpy(client_message, "");
if ((dir = opendir(".")) == NULL ) strcpy(client_message, "Unable to open the directory.\n");
else
{
while (de = readdir(dir)) {strcat(client_message, de->d_name); strcat(client_message, "\n"); }
}
closedir(dir);
send(sock, client_message, strlen(client_message), 0);
continue;
}
if (strcmp(client_message, "date") == 0)
{
time_t rtime;
rtime = time (NULL);
strcpy(client_message, ctime(&rtime));
send(sock, client_message, strlen(client_message), 0);
continue;
}
if (strcmp(client_message, "mkdir") == 0)
{
int result_code = mkdir("./new dir");
if (result_code == 0) strcpy(client_message, "OK");
else strcpy(client_message, "ERROR");
send(sock, client_message, strlen(client_message), 0);
continue;
}
if (strcmp(client_message, "exit") == 0)
{
strcpy(client_message, "Bye");
send(sock, client_message, strlen(client_message), 0);
break;
}
send(sock, client_message, strlen(client_message), 0);
if(read_size == 0)
{
printf("Client disconnected\n");
fflush(stdout);
break;
}
else if(read_size == -1)
{
printf("Recv failed\n");
}
}
printf("Thread will now be deleted.\n");
free(socket_d);
return 0;
}
截至目前,它似乎将这4个字符写入缓冲区和其他东西,使其长达5个字节。我对么?究竟发生了什么?我怎样才能让服务器处理任何长度的命令?
答案 0 :(得分:4)
截至目前,它似乎将这4个字符写入缓冲区和其他东西,使其长达5个字节。我是对的吗?
没有。 recv()
将最多读取所请求的字节数,但可能读取更少。来自链接的参考页面:
...接收电话通常会返回任何可用的数据,最多可达到请求的金额,而不是等待收到所请求的全部金额。
正在见证的其他是因为填充的缓冲区recv()
是单元化的:
message = malloc(5); /* Uninitialized. */
read_size = recv(sock , message , 5 , 0);
如果只读取4个字节,缓冲区中的第五个字节将包含一个随机值。
发布的代码错误地使用strcpy()
,这取决于是否存在空终止字符:使用recv()
的返回值确切地知道读取了多少字节以及它是否完全失败(其中返回值为-1
)的情况。例如:
read_size = recv(sock , message , 5 , 0);
if (read_size != -1)
{
if (read_size == 4 && memcmp(message, "list", 4) == 0)
{
}
}
else
{
/* Report failure. */
}
我怎样才能让服务器处理任何长度的命令?
为此,需要一种机制来指示消息的结束。这可以是指定的字符,不能出现在消息中,也可以在消息前面加上以下消息的长度。
答案 1 :(得分:3)
如果客户端发送包含4个字符的命令,而
recv()
函数被告知接收5个字节的数据,会发生什么?
缓冲区将包含4个字节的数据,recv()
返回的长度信息将告诉您有4个字节的有效数据,因此第五个字节中没有任何可靠信息(但最可能的是在recv()
来电之前就在那里。由于您的代码不会在以null结尾的字符串的末尾发送null,因此接收代码不会为空,因此您无法可靠地对接收的数据使用strcpy()
。使用memmove()
或memcpy()
或strncpy()
,然后空终止。
如果要发送任意长的消息,则有两个主要选项。一种是指定单个消息长度的上限,分配足够的空间来接收它,并仔细记录收到了多少。另一种是使用TLV(类型,长度,值)格式的变体,因此发送方指示数据的类型和长度加上实际数据,接收方可以读取类型和长度,分配足够的空间,然后读取值。