Программирование базового сокета в C

Таким образом, у меня возникла странная проблема, с которой я столкнулся в своей программе, которая в основном является примером программирования сокета для клиент / сервер в C. Я уделил время от проблемы, но я не могу найти причину проблемы.

Если нужно, я могу опубликовать более подробную информацию, но следующее – это то, что не работает.

while(1) { char word[256]; gets(word); printf("%s",word); if(strcmp(word, "TRANSLATE") == 0 || strcmp(word, "GET") == 0 ||strcmp(word, "STORE") == 0 || strcmp(word, "EXIT") == 0) { if(strcmp(word, "TRANSLATE") == 0) { if(send(sock , word , strlen(word) , 0)<0) { printf("Send Error\n"); return 1; } bzero(server_reply,2000); if(recv(sock , server_reply , 2000 , 0)<0) { puts("Recieving Error\n"); } //Print out 200ok message printf("%s\n", server_reply); //Get the first word gets(word2); while(strcmp(word2,"." )!= 0) { if(send(sock , word2 , strlen(word2) , 0)<0) { printf("Send Error\n"); return 1; } gets(word2); } if(recv(sock , server_reply , 2000 , 0)<0) { puts("Recieving Error\n"); } printf("%s\n", server_reply); //STILL NEED TO WORK ON PARSING OUT SPACES BECAUSE RESULT IS IN A STRING, JUST WITH SPACES. //ALSO NEED TO DO SERVER SIDE CAPITALIZATION } if(strcmp(word, "GET") == 0) { printf("Getting"); /* if(send(sock , word , strlen(word) , 0)<0) { printf("Send Error\n"); return 1; } bzero(server_reply,2000); if(recv(sock , server_reply , 2000 , 0)<0) { puts("Recieving Error\n"); } //Print out 200ok message printf("%s\n", server_reply); /* if(recv(sock , server_reply , 2000 , 0)<0) { puts("Recieving Error\n"); } //Print out the KANSAS message printf("%s\n", server_reply); */ } } 

Код разрывается, как только я раскомментирую выбор «GET», и он даже не попадает в оператор printf () перед оператором if. Если я его оставлю, он отлично справится со всеми правильными утверждениями. Я что-то пропустил? Кроме того, я думаю, что это может быть что-то с коммуникацией с сервером, так что это код сервера.

  #include #include //strlen #include #include //inet_addr #include //write int main(int argc , char *argv[]) { int socket_desc , client_sock , c , read_size; struct sockaddr_in server , client; char client_message[2000]; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) { printf("Could not create socket"); } puts("Socket created"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 5149 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) { //print the error message printf("bind failed. Error"); return 1; } puts("bind done"); //Listen listen(socket_desc , 3); //Accept and incoming connection puts("Waiting for incoming connections..."); c = sizeof(struct sockaddr_in); //accept connection from an incoming client client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); if (client_sock  0 ) { if(strcmp(client_message,"TRANSLATE")==0) { *(client_message + read_size) = '\0'; char word[256]="200 OK TRANSLATE"; write(client_sock, word, strlen(word)); /* while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 ) { *(client_message + read_size) = '\0'; puts(client_message); write(client_sock,client_message,strlen(client_message)); if(strcmp(client_message,".")==0) { printf("breaking"); break; } printf("IN while loop"); } printf("Out of while loop"); */ } if(strcmp(client_message,"GET")==0) { *(client_message + read_size) = '\0'; char word[256]="200 OK GET"; write(client_sock, word , strlen(word)); char word2[256]="I don't think we're in Kansas anymore."; write(client_sock, word2 , strlen(word2)); } if(strcmp(client_message,"STORE")==0) { char word[256]="200 OK STORE"; write(client_sock, word , strlen(word)); } } if(read_size == 0) { puts("Client disconnected"); fflush(stdout); } else if(read_size == -1) { printf("recv failed"); } return 0; } 

Вы должны добавить \n в конец каждой строки управления форматом printf и / или вызвать fflush (3)

Основной проблемой в вашей программе является неверное убеждение, что данная send (или write ) в сокет (7) соответствует одному recv (или read ) с другой стороны.

Реализация tcp (7) – это всего лишь stream байтов ; нет границ неявного сообщения. Поэтому ваше приложение должно буферизировать и явно разделить этот stream на значимые сообщения.

Может случиться так, что одна сторона может send или write 4000 байт одновременно, а затем другую эмиссию в 1000 байт, но другая сторона будет сначала recv (или read ), например, 39 байт, затем 1 байт, затем 1960 байт, а затем 3000 байт. Затем вы понимаете, что «сообщения» не являются нетронутыми, существует только stream байтов.

Еще одна трудность заключается в том, что такое поведение не воспроизводится. В следующий раз, когда вы запустите серверные и клиентские программы, они будут вести себя по-другому!

На практике полезно хорошо определить, где границы сообщений. Вы можете сделать это как в HTTP , т. Е. В начале каждого сообщения добавить некоторую длину контента (в HTTP, атрибут Content-Length: в заголовках ответов). Вы также можете решить, что сообщение представляет собой одну длинную строку с одной новой строкой, завершающей ее (и тогда вам может понадобиться соглашение, чтобы избежать внутренних строк новой строки), например, посмотрите на JSON . И, конечно же, вы должны буферизировать с обеих сторон.

Возможно, вам захочется проверить читаемость или возможность записи сокета (или канала) с помощью мультиплексирующего вызова типа poll (2) (возможно, в вашем цикле событий )

Я настоятельно рекомендую прочитать Advanced Linux Programming и некоторый учебник по Linux Socket .

BTW, всегда проверяйте свои системные вызовы на отказ. См. Intro (2) и используйте perror (3) или errno (3) (часто со страусом (3) ).

Конечно, скомпилируйте все предупреждения и информацию об отладке ( gcc -Wall -g ) и используйте отладчик ( gdb ) (и, возможно, также strace (1) ). Возможно, вам захочется отладить с двумя сеансами gdb (один для сервера, один для клиента) в разных терминалах.

BTW, не используйте устаревшую устаревшую функцию bzero , вместо этого используйте стандартную memset (3) .