Как правильно читать данные из сокета? - PullRequest
0 голосов
/ 08 апреля 2020

У меня есть простое приложение клиент / сервер. Пользователь пишет строки в консоли. Когда он нажимает, ввод строки отправляется. Я могу правильно передать одну строку, но после нее первая буква первого отправленного слова заменяется следующей. Например, если пользователь отправляет «Hello», сервер получит «Hello», но после, если я снова отправлю «Hello», сервер получит «HHello». Если я пытаюсь очистить буфер на стороне клиента после его отправки, он никогда не отправляет что-либо снова.

Код сервера:

// Server side C/C++ program to demonstrate Socket programming 
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>

#define PORT 57174
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
    perror("socket failed");
    exit(EXIT_FAILURE);
}


if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
               &opt, sizeof(opt)))
{
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );


if (bind(server_fd, (struct sockaddr *)&address,
         sizeof(address))<0)
{
    perror("bind failed");
    exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
    perror("listen");
    exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
                         (socklen_t*)&addrlen))<0)
{
    perror("accept");
    exit(EXIT_FAILURE);
}

char buffer[1024];
bzero(buffer, sizeof(buffer));
int step = 0;
   while(1){
   valread = read( new_socket , buffer, 1024);


   if(valread == 0)
       break;

   printf("%s", buffer );
printf("\n");

   bzero(buffer, sizeof(buffer));   


};

       return 0;
   } 

Код клиента:

// Клиентская программа C / C ++ для демонстрации программирования Socket

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>
#define PORT 57174

int main(int argc, char const *argv[]) 
{ 
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    { 
        printf("\n Socket creation error \n"); 
        return -1; 
    } 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));

    // Convert IPv4 and IPv6 addresses from text to binary form 
    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
    { 
        printf("\nConnection Failed \n"); 
        return -1; 
    }
//    char buffer[1024] = {0};

    unsigned int N = 10, delta=10, i = 0;
    char* buf = (char*) malloc (sizeof(char)*N);
    while (1)  {
        buf[i] = getchar();
        if(buf[i] == 27)
            break;

        if(buf[i] == 10){
            send(sock , buf , strlen(buf) , 0 );
//            bzero(buf, sizeof(buf));
            N = 10;
            buf = (char*) realloc (buf, sizeof(char)*N);
            i = 0;
        }

        if (++i >= N) {
            N += delta;
            buf = (char*) realloc (buf, sizeof(char)*N);
        }
    }



    return 0; 
}

1 Ответ

2 голосов
/ 08 апреля 2020

если пользователь отправляет «Hello», сервер получит «Hello», но после, если я снова отправлю «Hello», сервер получит «HHello»

This потому что вы пропустили else в вашем клиенте, в

   if(buf[i] == 10){
       send(sock , buf , strlen(buf) , 0 );
   //            bzero(buf, sizeof(buf));
       N = 10;
       buf = (char*) realloc (buf, sizeof(char)*N);
       i = 0;
   }
   if (++i >= N) {
       N += delta;
       buf = (char*) realloc (buf, sizeof(char)*N);
   }

вам нужно заменить

if (++i >= N) {

на

else if (++i >= N) {

в противном случае после того, как вы отправили свой буфер и установите i в 0, вы увеличите его, и вы запомните следующий символ с индексом 1, символ с индексом 0 все еще присутствует, и вы будете отправлять его снова и снова


У вас также есть проблема в вашем клиенте на

send(sock , buf , strlen(buf) , 0 );

потому что вы не помещаете нулевой символ в buff , необходимый для strlen для возврата ожидаемого значения, поэтому поведение не определено. На самом деле вам не нужно strlen , просто сделайте

 send(sock , buf , i , 0 );

, предполагая, что вы не хотите отправлять \ n


на стороне вашего сервера

char buffer[1024];
...
valread = read( new_socket , buffer, 1024);
if(valread == 0)
  break;
printf("%s", buffer );

вы каждый раз заполняете буфер нулевыми символами, но если вы читаете 1024 символа, в вашем буфере нет нулевого символа и printf go из буфера с неопределенным поведением

предупреждение чтение возвращает -1 при ошибке, valread == 0 неверно

удалите все ваши bzero просто сделайте

 char buffer[1024];
 ...
 while ((valread = read(new_socket, buffer, sizeof(buffer)-1)) > 0) {
   buffer[valread ] = 0;
   printf("%s", buffer);
 }

обратите внимание, что я использовал sizeof (буфер) вместо 1024, что позволяет быть уверенным в правильном размере даже при изменении размера буфера


Другие замечания для клиента:

  • переменная привет бесполезно

  • по определению sizeof (char) значения 1, поэтому sizeof (char) * N can быть замененным на N везде

  • не сравнивайте символ чтения с литералом 10 и 27, сравните с '\ n' и '\ e'

  • у вас нет управлять EOF на входе, для этого вам нужно сохранить символ чтения в int вместо char (например, buf [i] is) для сравнения это с EOF

На сервере переменная step бесполезна


Из-за этого вы используете SOCK_STREAM, поэтому ваш сокет является stream ( tcp not udp ), это означает, что вы не можете предполагать размер данных, которые вы читаете каждый раз, когда вызываете read, я имею в виду, если клиент отправил N байтов это не означает, что сервер будет читать N байтов при соответствующем чтении (если я могу сказать «соответствующий», потому что нет соответствия ;-)).

Предположим, что другие проблемы устранены, если вы введете azeqsd \ n , отправленные вами azeqsd , но, возможно, на стороне сервера вы прочтете azeq так что напечатайте azeq \ n и на следующем l oop вы прочтете sd и напечатаете sd \ n .

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

Вам это нужно? если нет, вам нужно отправлять размер перед каждым буфером, чтобы знать, сколько нужно читать, даже несколько раз, чтобы составить полный отправленный буфер (другое преимущество заключается в том, что вы не читаете байт на

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...