2014-02-04 2 views
0

Я пытаюсь написать сервер в C, используя сокеты, которые будут получать команды от нескольких клиентов. Я пытаюсь понять: если клиент отправляет команду, состоящую, скажем, из 4 символов, а функции recv() говорят, чтобы получить 5 байтов данных, что произойдет?Что делает recv() запись в полученные данные буфериза меньше длины буфера

Вот мой код:

#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-х символов в буфер и что-то еще, чтобы сделать его длиной пять байт. Я прав? Что именно происходит? Как я могу сделать так, чтобы сервер мог обрабатывать команды любой длины?

+0

Пятый байт не изменился - он по-прежнему имеет значение, полученное перед вызовом recv() – Arkadiy

ответ

4

На данный момент кажется, что он напишет эти 4 символа в буфер и что-то еще, чтобы сделать его длиной в пять байтов. Я прав?

No. recv() прочтет до максимума числа запрошенных, но может читать меньше. Из связанной справочной страницы:

... Звонки на звонки обычно возвращают любые доступные данные, вплоть до запрашиваемой суммы, а не ждут получения полной запрашиваемой суммы.

что-то еще будучи свидетелем, потому что буфер recv() заполняемый является неинициализированным:

message = malloc(5); /* Uninitialized. */ 
read_size = recv(sock , message , 5 , 0); 

Meaing если только 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. */ 
} 

Как я могу сделать так, чтобы сервер мог обрабатывать команды любой длины?

Для этого необходим механизм, указывающий на конец сообщения. Это может быть обозначенный символ, который не может отображаться внутри сообщения или путем префикса сообщения длиной следующего сообщения.

+0

Если я правильно понял, использование strncpy (client_message, message, read_size) вместо strcpy() должно делать трюк, правильно? – FalconD

+0

Что делать, если для нулевого символа недостаточно места? Из ['strncpy()'] (http://en.cppreference.com/w/c/string/byte/strncpy): _ Если счетчик достигнут до того, как скопирована вся строка src, результирующий массив символов не равен null, terminated._ – hmjd

+1

@ user3201961: помните, 'strncpy()' не гарантирует выходную строку с нулевым завершением. Очень важно помнить, что, поскольку самая распространенная ошибка при использовании 'strncpy()' заключается в том, чтобы забыть, чтобы строка была завершена нулем. Другая проблема с 'strncpy()' заключается в том, что если в качестве целевого объекта имеется буфер 64 KiB, 'strncpy()' будет писать 64 KiB каждый раз, когда он копирует, даже если исходная строка имеет 3 байта плюс нулевой байт длиной. Если вы этого не знали, внимательно прочитайте спецификацию 'strncpy()'. –

3

Если клиент отправляет команду, состоящую, скажем, из 4 символов, тогда как функция recv() получает 5 байт данных, что произойдет?

Буфер будет содержать 4 байта данных и информацию о длине, возвращенном recv() сообщит вам, что есть 4 байта действительных данных и, следовательно, нет ничего надежно в пятом байте (но это, скорее всего, что было там до звонка recv() был сделан). Поскольку ваш код не отправляет нуль в конце строки с нулевым завершением, код получения не получает нуль, поэтому вы не можете надежно использовать strcpy() на полученных данных.Используйте memmove() или memcpy() или, возможно, strncpy(), а затем завершите нуль.

Если вы хотите отправлять произвольно длинные сообщения, у вас есть два основных варианта. Один из них - указать верхнюю границу длины одного сообщения, выделить достаточно места для его получения и тщательно отметить, сколько было получено. Другой вариант - использовать вариант в формате TLV (тип, длина, значение), чтобы отправитель указывал тип и длину данных плюс фактические данные, а получатель мог читать тип и длину, выделять достаточно места, а затем читать Значение.