2012-01-20 25 views
1

最近、スレッド化を使用してサーバーからメッセージを送受信するためのクライアント側コードについて作業しています。以下のコードは実行時に不思議な動作をします。サーバーに送信するメッセージを入力すると、コードはタスクを完了しますが、「ソケットはすでに使用中です」というエラーが発生しても、サーバーはそれを取得します。しかし、サーバーに送信しようとするそれ以降のすべてのメッセージはすぐには受信されませんが、クライアントプログラムが終了すると一度に受信されるようです。アドレスは既に使用中です。

(また、私は1つの出力機能をコメントした場合奇妙な行動が発揮されず、エラーがクライアント側で特定しています。)

は、どのように私はこのエラーを修正することができます

クライアント

#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

それは単なる 'cout'文を除いて純粋なCです。 – vines

答えて

0

私はあなたが与えている "SO_REUSEADDR" ソケットオプションが問題であることを推測します。

クライアントソケットを閉じずにこの関数を何度も呼び出していますか?その場合は動作しません。

このソケットオプションの目的は、「同じアドレスの既に開いているソケットがTIME_WAIT状態にあるときにアドレスを再利用する」ことです。そうでなければ、上記のエラーが発生します。

あなたのクライアントは、毎回新しい接続を開いている場合は、私はあなたがより効率的にあなたのコードを構造化し、同様にソケット閉鎖シナリオを処理する必要がありますと言わなければなりません。

3

クライアントのbind()をサーバーと同じポートにしようとしているようです。それは必要ではありません。さらに悪いことに、サーバーのIPアドレスにバインドしようとしています。これもまた大きな問題です。一般に、connect()関数を呼び出すクライアントソケットの場合は、ソケット0とIP 0をバインドするだけで、OSがランダムに使用可能なポートを選択し、正しいローカルIPアドレスを使用できるようにする必要があります。接続のためのアダプタ。 getsockname()を呼び出して、connectを呼び出した後にOSがあなたのために選択したポートを見つけることができます。

OSにクライアントポートを選択させると、そのSO_REUSESADDRコールは必要ありません。ただし、サーバーコードは、接続が終了するまで保留中の接続を使用してシャットダウン後に再起動する必要がある場合に呼び出すことができます。

また、いずれのソケット呼び出しの戻り値もチェックしていません。それはおそらくあなたが神秘的な結果を得ている理由です。 bind()の呼び出しは、サーバIPを指定しているので失敗する可能性が高くなりますが、connect()は、ソケットがまだ存在しない場合に自動的にバインドするため成功しています。

ここでは、あなたがきれいになったバージョンのinput()関数があります。あなたの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; 
} 
関連する問題