web-dev-qa-db-ja.com

ソケットプログラミング:CでのUDPクライアントサーバー

UDPと待機停止を使用してクライアントサーバープログラムを作成しようとしていますが、その部分に達していません。2つのプロセス(サーバーとクライアント)がどのように通信するかを理解しようとしています。クライアントプログラムでは、ユーザーはサーバー名またはIPアドレス、およびポート名を入力し、サーバーが計算する式を送信する必要があるためです。しかし、インターネットでいくつかのチュートリアルを掘り、それに応じてコーディングした後(またはそう思った)、クライアントをサーバーと通信させることができません。以下は私のコードですが、bind()sendto()recvfrom()またはsocket()の場合、私が間違っていることを教えてくださいまたはそれらすべて。何が悪いのかわかりません。クライアント側が無限ループで実行されるべきではないことはわかっていますが、これまでのところ、プログラムを相互に通信させたいので、コードを磨きます。ありがとう!

クライアント側コード:

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

#define EXPR_SIZE   1024
#define BUFLEN      512
#define TRUE        1
#define FALSE       0
#define SERVERLEN   1024

int main(int argc, char **argv){

    long portNum;           // Since it's possible to input a value bigger
                            // than 65535 we'll be using long to
                            // avoid overflows
    char expr[EXPR_SIZE];
    char server[SERVERLEN];
    int fd;                 // file descriptor for the connected socket
    int buf[512];
    struct hostent *h;           // information of the Host
    unsigned int addrLen;        // address length after getting the port number
    struct sockaddr_in myaddr;   // address of the client
    struct sockaddr_in servaddr; // server's address
    unsigned int exprLen;
    socklen_t slen = sizeof(servaddr);

    printf("Enter server name or IP address:");
    scanf("%s",server);
    printf("Enter port:");
    scanf("%ld",&portNum);
    if ((portNum < 0) || (portNum > 65535)) {
        printf("Invalid port number. Terminating.");
        return 0;
    }
    printf("Enter expression:");
    scanf("%s",expr);

    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
        perror("cannot create socket");
        return 0;
    }

    memset((char *)&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(0);

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
        perror("cannot bind");
        return 0;
    }

    /*
     // Discovering the port number the OS allocated
     addrLen = sizeof(myaddr);
     if(getsockname(fd, (struct sockaddr *)&myaddr, &addrLen) < 0){
     perror("cannot getsockname");
     return 0;
     }
     printf("local port number = %d\n", ntohs(myaddr.sin_port));
     */

    memset((char*)&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htonl(portNum);

    exprLen = sizeof(expr);


    while(TRUE){
        printf("Sending message to %s port %ld\n",server, portNum);
        if (sendto(fd, expr, strlen(expr), 0, (struct sockaddr *)&servaddr, slen) < 0) {
            perror("cannot sendto()");
        }
        printf("Success\n");

    }


    return 0;
}

サーバー側コード:

#include <stdio.h>      // Default System Calls
#include <stdlib.h>     // Needed for OS X
#include <string.h>     // Needed for Strlen
#include <sys/socket.h> // Needed for socket creating and binding
#include <netinet/in.h> // Needed to use struct sockaddr_in
#include <time.h>       // To control the timeout mechanism

#define EXPR_SIZE   1024
#define BUFLEN      512
#define TRUE        1
#define SERVERLEN   1024

int main(int argc, char **argv){

    struct sockaddr_in myaddr;  // address of the server
    struct sockaddr_in claddr;  // address of the client
    char buf[BUFLEN];
    int fd;
    long recvlen;
    socklen_t clientlen;



    if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
        perror("cannot create socket");
        return 0;
    }

    memset((char *)&myaddr, 0, sizeof(myaddr));
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(0);

    if(bind(fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0){
        perror("cannot bind");
        return 0;
    }
    clientlen = sizeof(claddr);

    while (TRUE) {
        recvlen = recvfrom(fd, buf, BUFLEN, 0, (struct sockaddr *)&claddr, &clientlen);
        if (recvlen < 0) {
            perror("cannot recvfrom()");
            return 0;
        }
        printf("Received %ld bytes\n",recvlen);
        buf[recvlen] = 0;
        printf("Received message: \"%s\"\n",buf);

    }

    return 0;
}

サーバープログラムは何も出力しませんが、クライアントはプロセスが中断されるまで出力します。

Enter server name or IP address:127.0.0.1
Enter port:30
Enter expression:2+2
Sending message to 127.0.0.1 port 30
cannot sendto(): Can't assign requested address

サーバー名をlocalhostやその他のポートに変更しようとしましたが、役に立ちませんでした。

7
William Studart

ネットワークソフトウェアを開発する場合(特にBSDソケットインターフェイスを使用する場合)、基本的な通信を確立するまでは、物事をできるだけシンプルに保つことが重要です。次に、機能を段階的に追加しながら、途中で何も壊さないようにします。

クライアント側では、物事をシンプルに保つことが重要です

  • クライアントでbindを呼び出さないでください。 OSは適切なインターフェースを選択し、ランダムなポート番号を割り当てるため、ソケットをbindする必要はありません。

  • ハードコーディングされたサーバーアドレスを使用します(例:127.0.0.1)。アドレス127.0.0.1(0x7f000001)はローカルホストアドレスで、同じマシン上のサーバーにパケットを送信するのに適しています。

  • ハードコードされたポート番号(50037など)を使用します。 エフェメラルポート番号 は0xC000 16進数(10進数49152)より大きい必要があります。

  • ハードコードされたメッセージを使用してください。 "こんにちは"。

これを念頭に置いて、クライアントソフトウェアは次のようになります。

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror("socket failed");
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );              
    serveraddr.sin_addr.s_addr = htonl( 0x7f000001 );  

    for ( int i = 0; i < 4; i++ ) {
        if (sendto( fd, "hello", 5, 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
            perror( "sendto failed" );
            break;
        }
        printf( "message sent\n" );
    }

    close( fd );
}

サーバー側では、物事をシンプルに保つということは

  • INADDR_ANYにバインドします。つまり、OSに適切なインターフェースを選択させます。
  • ハードコードされたポートにバインドします。 50037(クライアントが使用するポートと同じである必要があります)。
  • recvfromからアドレス情報を要求しないでください。つまり、最後の2つのパラメーターとしてNULL, 0を渡します。

これを念頭に置いて、サーバーソフトウェアは次のようになります。

int main( void )
{
    int fd;
    if ( (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        perror( "socket failed" );
        return 1;
    }

    struct sockaddr_in serveraddr;
    memset( &serveraddr, 0, sizeof(serveraddr) );
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons( 50037 );
    serveraddr.sin_addr.s_addr = htonl( INADDR_ANY );

    if ( bind(fd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0 ) {
        perror( "bind failed" );
        return 1;
    }

    char buffer[200];
    for ( int i = 0; i < 4; i++ ) {
        int length = recvfrom( fd, buffer, sizeof(buffer) - 1, 0, NULL, 0 );
        if ( length < 0 ) {
            perror( "recvfrom failed" );
            break;
        }
        buffer[length] = '\0';
        printf( "%d bytes: '%s'\n", length, buffer );
    }

    close( fd );
}
15
user3386109