3

UDPを使ってネットワークシステムを作ろうとしています。なんとか動作させることができましたが、メッセージを待たずにプログラムを続行できるように、リッスン部分を別のスレッドに入れたいと思います (ゲームに使用されます)。

問題は、ソケットを新しいスレッドに渡してリッスンに使用すると、それが機能せず、常にランダムな入力を受け取ることです。ソケットを初期化するメソッドでまったく同じコードを使用すると、すべて正常に動作します。

これは機能します:

bool serverUDP() {
WSADATA w;
SOCKET sd;

struct sockaddr_in server, client;
char buffer[DEFAULT_BUFLEN];
int bytes_received;
int client_length;

/* Open windows connection */
if (WSAStartup(MAKEWORD(2,2)/*0x0101*/, &w) != 0) {
    fprintf(stderr, "Could not open Windows connection.\n");
    return false;
}

/* Open a datagram socket */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd == INVALID_SOCKET) {
    fprintf(stderr, "Could not create socket.\n");
    WSACleanup();
    return false;
}

/* Clear out server struct */
memset((void *)&server, '\0', sizeof(struct sockaddr_in));

/* Set family and port */
server.sin_family = AF_INET;
server.sin_port = htons(atoi(DEFAULT_PORT));

server.sin_addr.S_un.S_un_b.s_b1 = 127;
server.sin_addr.S_un.S_un_b.s_b2 = 0;
server.sin_addr.S_un.S_un_b.s_b3 = 0;
server.sin_addr.S_un.S_un_b.s_b4 = 1;

/* Bind address to socket */
if (bind(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1) {
    fprintf(stderr, "Could not bind name to socket.\n");
    closesocket(sd);
    WSACleanup();
    return false;
}

client_length = (int)sizeof(struct sockaddr_in);

/* Receive bytes from client */
while (true) {
    bytes_received = recvfrom(sd, buffer, DEFAULT_BUFLEN, 0, (struct sockaddr *)&client, &client_length);
    fprintf(stderr,"received: %s\n", buffer);
}

closesocket(sd);
WSACleanup();   

return true;
}

これはしません:

bool serverUDP() {
WSADATA w;
SOCKET sd;

struct sockaddr_in server, client;
char buffer[DEFAULT_BUFLEN];
int bytes_received;
int client_length;

/* Open windows connection */
if (WSAStartup(MAKEWORD(2,2)/*0x0101*/, &w) != 0) {
    fprintf(stderr, "Could not open Windows connection.\n");
    return false;
}

/* Open a datagram socket */
sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd == INVALID_SOCKET) {
    fprintf(stderr, "Could not create socket.\n");
    WSACleanup();
    return false;
}

/* Clear out server struct */
memset((void *)&server, '\0', sizeof(struct sockaddr_in));

/* Set family and port */
server.sin_family = AF_INET;
server.sin_port = htons(atoi(DEFAULT_PORT));

server.sin_addr.S_un.S_un_b.s_b1 = 127;
server.sin_addr.S_un.S_un_b.s_b2 = 0;
server.sin_addr.S_un.S_un_b.s_b3 = 0;
server.sin_addr.S_un.S_un_b.s_b4 = 1;

/* Bind address to socket */
if (bind(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1) {
    fprintf(stderr, "Could not bind name to socket.\n");
    closesocket(sd);
    WSACleanup();
    return false;
}

_beginthread((void(*)(void*))receiveThread,0,(void *)sd);

closesocket(sd);
WSACleanup();   

return true;
}

void receiveThread(SOCKET sd)
{
struct sockaddr_in client, server;
char buffer[DEFAULT_BUFLEN];

int bytes_received;
int client_length;

client_length = (int)sizeof(struct sockaddr_in);

while (true) {
    bytes_received = recvfrom(sd, buffer, DEFAULT_BUFLEN, 0, (struct sockaddr *)&client, &client_length);
    fprintf(stderr,"received: %s\n", buffer);
}
}

コードの 2 番目の部分は、あたかも何らかの奇妙なメッセージを常に受け​​取っているかのように、ランダムな入力をスパムするだけです。コードの最初の部分は、メッセージを待機し、メッセージを取得すると続行します (クライアント部分もあります)。

ご協力いただきありがとうございます!

乾杯、

マキシム・シューメーカー

4

5 に答える 5

4

メインスレッドは、他のスレッドを開始した直後にソケットを閉じます。したがって、スレッドには有効なソケット ハンドルがありません。

于 2012-06-29T16:56:58.270 に答える
3

問題の原因は次の行です。

_beginthread((void(*)(void*))receiveThread,0,(void *)sd);

基本的に、間違った引数で関数を呼び出してreceiveThread()いますが、コンパイラをだましてエラーを検出できない状況に陥らせました。関数を間違ったシグネチャにキャストし、パラメーターを間違った型にキャストしています。

receiveThread()適切な署名を持つように関数を変更してから、キャストを削除してください。

void * receiveThread(void * context);

_beginthread(receiveThread,0,(void *)sd);

次に、ポインタをsd変数に渡し、スレッド関数でそれを参照します。

_beginthread(receiveThread,0,&sd);


void * receiveThread(void * context)
{
    SOCKET socket = *(SOCKET*)context;
    // ...
}
于 2012-06-29T16:45:19.553 に答える
1
(void *)&sd

void receiveThread(void *param)

SOCKET sd = *(SOCKET *)param;

私はそれを修正する必要があると思います!

編集:

(void *)sd と書くと、sd 値のアドレスへのポインタが作成されますが、これは間違っています。sd のアドレスへのポインタを作成する必要があるため、&sd と書くと、スレッド関数を使用できなくなったら、その値を取得できます。それを逆参照することによって: SOCKET sd = (SOCKET)*param;

私の英語はそれほど上手ではありません。理解していただければ幸いです。

于 2012-06-29T16:40:49.360 に答える
0

プログラムは次のように読み取る必要があります(sudoコード):

 while(1){
 listen()
 newSD = accept()
 pthread_creat(newsd)

}

これにより、メインを実行したまま、接続ごとに新しい新しいスレッドが作成されます

于 2012-08-23T01:14:34.533 に答える
0

メイン関数でソケットを閉じる前に、WaitForSingleObject() を含める必要があります。

于 2017-04-25T20:21:09.370 に答える