recv()写入缓冲区数据的内容小于缓冲区的长度

时间:2014-02-04 15:08:49

标签: c sockets recv

我试图使用套接字在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个字节。我对么?究竟发生了什么?我怎样才能让服务器处理任何长度的命令?

2 个答案:

答案 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(类型,长度,值)格式的变体,因此发送方指示数据的类型和长度加上实际数据,接收方可以读取类型和长度,分配足够的空间,然后读取值。