Программирование сокетов: проблема с приемом/чтением

EDIT: приведенный ниже код был исправлен для правильного получения и отправки И для учета фактических байтов отправленных и полученных сообщений (последнее благодаря EJP)

Я программирую на C в Unix.

У меня есть сервер и клиент, которые должны обмениваться сообщениями. Хотя клиент, кажется, отправляет сообщения нормально, сервер не получает сообщения, которые отправляет клиент. Я пытался использовать recv() и read() (я знаю, что это практически одно и то же, но с дополнительными флагами на recv()), но мне не повезло, и я не совсем уверен, в чем проблема на самом деле.

Я ставлю sleep(3) в клиентский код каждый раз, когда он отправляет сообщение, но я вижу, что как только клиент и сервер соединяются, сервер немедленно закрывается, не дожидаясь входящих сообщений. Что я делаю неправильно?

Это клиентский код:

#define SERVER_TCP_PORT 11112
#define MAX_DATA_SIZE   500

int main(int argc, char * argv[])
{
    int sockfd;
    char * host;
    char msg[MAX_DATA_SIZE];/* = "get my msg!\n";*/
    int msg_len;

    struct hostent * hp;
    struct sockaddr_in client_address, server_address;


    printf("y halo thar\n");


    // looking up from the host database
    if (argc == 2)
        host = argv[1];
    else
        exit(1);
    printf("sdf\n");


    hp = gethostbyname(host);
    if (!hp)
        exit(1);
    printf("host found\n");


    // setting up address and port structure information
    bzero((char * ) &server_address, sizeof(server_address)); // copy zeroes into string
    server_address.sin_family = AF_INET;
    bcopy(hp->h_addr, (char *) &server_address.sin_addr, hp->h_length);
    server_address.sin_port = htons(SERVER_TCP_PORT);
    printf("set\n");


    // opening up socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
        exit(1);
    printf("opened\n");


    // connecting
    if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
        exit(1);
    printf("connected\n");


    int i;

    for (i = 0; i < MAX_DATA_SIZE; ++i)
    {
        msg[i] = '.';
    }

    msg[MAX_DATA_SIZE-1] = '\0';

    for(i = 0; i < 11; i++)
    {
        // send message to connected socket
        msg_len = write(sockfd, msg, MAX_DATA_SIZE);
        if(msg_len < 1)
            printf("notsent\n");
        else
            printf("%i  bytes sent\n", msg_len);

        // recieve messages from connected socket
        msg_len = read(sockfd, msg, MAX_DATA_SIZE);
        if (msg_len < 1)
            printf("not recieved\n");
        else
        {
            printf("%i bytes received\n", msg_len);
            printf(msg);
            printf("\n");

        }
    }


    // close connection
    close(sockfd);
    printf("closed\n");

}

а это серверная часть

#define SERVER_TCP_PORT 11112
#define MAX_DATA_SIZE   500


int main()
{

    printf("o halo thar\n");

    int sockfd, new_sockfd;
    int client_addr_len;
    char msg [MAX_DATA_SIZE];
    int msg_len;
    char got_msg [11] = "got ur msg\0";
    struct sockaddr_in server_address, client_address;


    // setting up address and port structure information
    bzero((char * ) &server_address, sizeof(server_address)); // copy zeroes into string
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(SERVER_TCP_PORT);


    // opening up socket
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
        exit(1);
    printf("socket is opened\n");


    // binding
    if (bind(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0)
        exit(1);
    printf("socket is bound\n");


    // listening
    listen(sockfd,5);
    printf("listening\n");

    // block and wait for an incoming connection

    client_addr_len = sizeof(client_address);
    new_sockfd = accept(sockfd, (struct sockaddr *) &client_address, &client_addr_len);
    if (new_sockfd < 0)
        exit(1);

    printf("accepted\n");


    int i;

    for( i = 0; i < 11; i++)
    {
        // recieve messages from connected socket
        printf("waiting\n");
        msg_len = read(new_sockfd, msg, MAX_DATA_SIZE);
        if (msg_len < 1)
        {
            printf("no msg recieved\n");    
        }
        else
        {
            printf("bytes recieved: %i\n", msg_len);
        }


        // send message to connected socket
        msg_len = write(new_sockfd, got_msg, sizeof(got_msg));
        if (msg_len < 1)
            printf("not sent\n");
        else
            printf("%i bytes sent\n", msg_len);
    }


    // close connection
    close(sockfd);
    printf("socket closed. BYE! \n");


}

person Fantastic Fourier    schedule 01.03.2010    source источник


Ответы (5)


В коде сервера проблема в этой строке:

msg_len = read(sockfd, msg, MAX_DATA_SIZE);

Вы вызываете read на sockfd, но вам нужно вызвать read или recv на new_sockfd (сокет, возвращаемый accept()). new_sockfd — это тот, который подключен к клиенту (sockfd используется для принятия дальнейших подключений — например, если подключается другой клиент).

person caf    schedule 01.03.2010
comment
Спасибо решил проблему!!! И да, гораздо логичнее читать новый файловый дескриптор сокета из accept(). - person Fantastic Fourier; 01.03.2010
comment
Вы также игнорируете счетчики, возвращаемые read() и write(), кроме проверки их на наличие ошибок. Вы не можете этого сделать: вы должны использовать их, чтобы определить, сколько данных было фактически прочитано или записано. Вы не можете считать, что ваша просьба была полностью выполнена. - person user207421; 01.03.2010
comment
EJP код теперь учитывает фактические байты отправленных и полученных сообщений. Спасибо. - person Fantastic Fourier; 01.03.2010

Вы должны читать из сокета, возвращенного accept.

Попробуйте позвонить read по телефону socket, возвращенному accept.

person stefanB    schedule 01.03.2010
comment
Сообщение, отправленное клиентом, явно начинается с символа '.'. - person caf; 01.03.2010

Сторона приемника:

   while(1)
    {       
        len=read(sd,buff,sizeof(buff));

        if(len==0)
        {
            //End of File receving.             

            break;
        }
        else    
        {   
            st=fwrite(buff,sizeof(char),len,fp);

        }
    }
person Mukund    schedule 15.03.2013

Сторона отправки:

while(!feof(fp))
{
    len=fread(buff,sizeof(char),MW,fp);
    if(len==0)
    {
      //EOF
      st=write(cd,&d,sizeof(int));      
      break;
    }
    else
    {
        st=write(cd,buff,len);
    }
}
person Mukund    schedule 15.03.2013

реализация основана на потоке или дейтаграмме?

есть некоторые проблемы с вашим потоком операций. сервер может начать чтение до того, как клиент отправит что-либо.

поскольку клиент и сервер разделены, вы можете представить, что они работают одновременно. сразу после того, как ваша серверная сторона «примет» запрос на соединение, могут возникнуть некоторые накладные расходы на рукопожатие или задержки в сети, заставляющие серверное приложение выполняться раньше времени, пытаться извлечь данные, но встречаться с ошибками (данные еще не получены). вы можете попробовать это, добавив сон в код сервера после принятия соединения, когда у клиента должно быть достаточно времени для отправки данных.

другое лучшее решение — заставить поиск данных справляться с пустым буфером или асинхронным чтением.

person YeenFei    schedule 01.03.2010
comment
Нет никаких проблем с тем, что сервер вызывает read до того, как клиент что-либо отправит. Вызов read будет просто блокироваться до тех пор, пока не будут доступны данные. Отсутствие полученных данных не является ошибкой. - person caf; 01.03.2010
comment
И спать тоже не надо. - person user207421; 01.03.2010
comment
Я только добавил sleep() на стороне клиента, чтобы определить, в чем проблема, но я действительно не могу (и не должен) использовать sleep(), поскольку целью этой программы является измерение круговых поездок пакетов. - person Fantastic Fourier; 01.03.2010