2012-01-20 16 views
1

Недавно я работал над некоторым клиентским кодом для отправки и получения сообщений с сервера с помощью потоковой передачи. При запуске код ниже ведет себя странно. После ввода сообщения для отправки на сервер код завершает задачу, хотя и с ошибкой «socket уже используется», сервер получает ее. Но каждое последующее сообщение, которое я пытаюсь отправить на сервер, не принимается немедленно, но оно, похоже, все сразу получено, когда клиентская программа завершается.Адрес уже используется.

(Кроме того, я уверен, ошибка на стороне клиента, странное поведение не проявляется, если один комментарий функцию выхода.)

Как я могу исправить эту ошибку?

Client

#include <stdio.h> 
#include <cstdlib> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/time.h> 
#include <unistd.h> 
#include <netdb.h> 
#include <arpa/inet.h> 
#include <string> 
#include <iostream> 
#include <errno.h> 
#include <pthread.h>  
void* input(void* ptr) 
{ 
    int on = 1; 
    bool *input_done = ((struct thread_args*)ptr)->process_done; 
    struct addrinfo *res = ((struct thread_args*)ptr)->result; 
    char msg[256]; 
    int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol); 
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on)); 
    bind(sock,res->ai_addr,res->ai_addrlen); 
    connect(sock,res->ai_addr,res->ai_addrlen); 
    cin.getline(msg,256); 
    if (msg[0] == '/') {exit(1);} 
    send(sock,msg,sizeof msg,0); 
    cout << "You:" << msg << endl; 
    *input_done = 1; 
    close(sock); 
    pthread_exit(NULL); 
} 
void* output(void* ptr) 
{ 
     int on = 1; 
     bool *output_done = ((struct thread_args*)ptr)->process_done; 
    struct addrinfo *res = ((struct thread_args*)ptr)->result; 
    char msg[256]; 
    int sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol); 
    bind(sock,res->ai_addr,res->ai_addrlen); 
    connect(sock,res->ai_addr,res->ai_addrlen); 
    recv(sock,msg,sizeof msg,0); 
    cout << "Recieved:" << msg; 
    *output_done = 1; 
    close(sock); 
    pthread_exit(NULL); 
} 

void io_client() 
{ 
    //thread function variables 
    pthread_t t1,t2; 
    bool input_done = 1, output_done = 1; 
    //socket setup variables 
    struct addrinfo hints, *res; 
    memset(&hints,0,sizeof hints); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 
    getaddrinfo("localhost","8080",&hints,&res); 
    //setting up structures to pass data to threaded functions 
    struct thread_args i_args, o_args; 
    i_args.result = res; i_args.process_done = &input_done; 
    o_args.result = res; o_args.process_done = &output_done; 
    while(1) 
    { 
     if (output_done) 
     { 
      pthread_create(&t2,NULL,output,&o_args); 
      output_done = 0; 
     } 
     if (input_done) 
     { 
      pthread_create(&t1,NULL,input,&i_args); 
      input_done = 0; 
     } 
    } 
} 
int main() 
{ 
    io_client(); 
} 

Сервер

void server() 
{ 
    struct addrinfo hints, *res; 
    int sock=-1, newsock=-1; 
    int length, on=1; 
    char **address_list; int entries = 0; 
    //fd_set read_fd; 
    //struct timeval timeout; 
    char buffer[100]; 
    memset(&hints,0,sizeof hints); 
    res = NULL; 
    memset(&res,0,sizeof res); 
    hints.ai_family = AF_INET; 
    hints.ai_socktype = SOCK_STREAM; 
    getaddrinfo("localhost","8080",&hints,&res); 
    sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol); 
    setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on)); 
    bind(sock,res->ai_addr,res->ai_addrlen); 
    listen(sock,10); 
    while(1) 
    { 
     struct sockaddr_storage addr; 
     char ipstr[INET6_ADDRSTRLEN]; 
     socklen_t len; 
     len = sizeof addr; 
     newsock = accept(sock,NULL,NULL); 
     getpeername(newsock,(struct sockaddr*)&addr,&len); 
     struct sockaddr_in *s = (struct sockaddr_in*)&addr; 
     inet_ntop(AF_INET,&s->sin_addr,ipstr,sizeof ipstr); 
     length = 100; 
     setsockopt(newsock,SOL_SOCKET,SO_RCVLOWAT, (char*)&length,sizeof length); 
     recv(newsock,buffer,sizeof buffer,0); 
     cout << buffer << endl; 
    } 
    if (newsock != -1) 
    { 
     close(newsock); 
    } 
    if (sock != -1) 
    { 
     close(sock); 
    } 
} 
int main() 
{ 
    server(); 
} 
+1

Это не ответ на ваш вопрос, но я предлагаю вам использовать Boost.Asio. =) –

+0

И это чисто C, за исключением одного оператора 'cout'. – vines

ответ

0

Я предполагаю, что "SO_REUSEADDR" опция сокета, что вы даете это проблема.

Вы вызываете эту функцию снова и снова, не закрывая клиентский сокет? В этом случае это не сработает.

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

Если клиент открывает новое соединение каждый раз, то я должен сказать, что вы должны структурировать ваш код более эффективно и обрабатывать сценарии закрытия сокета, а также.

3

Похоже, вы пытаетесь связать ваш клиент() с тем же портом, что и сервер. Это не обязательно. И что еще хуже, вы пытаетесь привязать к IP-адресу сервера, что также является большой проблемой. В общем случае для клиентских сокетов, которые должны вызывать функцию connect(), вы должны просто привязать свой сокет к порту 0 и IP 0, таким образом позволяя ОС выбрать случайно доступный порт для вас и разрешить использование правильного локального IP-адреса и адаптер для подключения. Вы можете вызвать getsockname(), чтобы узнать, какой порт выбран для вас после вызова connect.

И если вы позволите ОС выбрать порт клиента для вас, вам не понадобится вызов SO_REUSESADDR. Хотя ваш код сервера может вызвать его для случаев, когда он должен перезагрузиться после завершения соединения с еще закрытыми соединениями.

Также. вы не проверяете возвращаемое значение любого из ваших вызовов сокетов. Вероятно, поэтому вы получаете некоторые загадочные результаты. Вызов bind() скорее сбой, потому что вы указываете IP-адрес сервера, но connect() преуспевает, потому что он автоматически привяжет сокет, если он еще не был.

Вот убираемая версия функции ввода(). Преобразование функции output() - это упражнение, оставленное читателю. Если вы последуете моему примеру, вы будете в хорошей форме.

void* input(void* ptr) 
{ 
    int on = 1; 
    bool *input_done = ((struct thread_args*)ptr)->process_done; 
    int ret; 
    int success = true; 

    struct sockaddr_in addrLocal = {}; 

    struct addrinfo *res = ((struct thread_args*)ptr)->result; 
    char msg[256]; 

    int sock = socket(AF_INET, SOCK_STREAM, 0); 
    success = (sock != -1); 

    if (success) 
    { 
     addrLocal.sin_family = AF_INET; 
     addrLocal.sin_port = INADDR_ANY;  // INADDR_ANY == 0 --> pick a random port for me 
     addrLocal.sin_addr.s_addr = INADDR_ANY; // INADDR_ANY == 0 --> use all appropriate network 
     ret = bind(sock,(sockaddr*)&addrLocal,sizeof(addrLocal)); 
     if (ret == -1) perror("bind: "); 
     success = (ret != -1); 
    } 

    if (success) 
    { 
     ret = connect(sock,res->ai_addr,res->ai_addrlen); 
     if (ret == -1) perror("connect: "); 
     success = (ret != -1); 
    } 

    if (success) 
    { 
     cin.getline(msg,256); 
     if (msg[0] == '/') {exit(1);} 
     ret = send(sock,msg,sizeof msg,0); 
     if (ret == -1) perror("send: "); 
     success = (ret != -1); 
    } 

    if (success) 
    { 
     cout << "You:" << msg << endl; 
     *input_done = 1; 
    } 

    if (sock != -1) 
    { 
     close(sock); 
     sock = -1; 
    } 

    return NULL; 
} 
Смежные вопросы