2011/10/25

Linuxネットワークプログラミング(TCPサーバー・pthread)

今日はLinuxのネットワークプログラミングについて調査、というか読書してました。
参考にしたのはTCP/IP Sockets in C という本です。
Kindle Storeで20ドルぐらいでした。紙の本で買うとすると、4週間ぐらい待たないとダメなようです。


ざ〜っと見た感じでは、Linuxのネットワークプログラミングも、Windowsのネットワークプログラミングとそんなに変わらないようなので安心。

ただ、スレッドの作り方がちょっとめんどそうな感じです。
スレッドの調査はまた後でするとして、まずは単純動作のサーバーアプリを作る(コピペする)ことにしました。

 
下記のサイトにあるサンプルコードを、まとめて1つのソースファイルにしています。
TCP/IP Sockets in C: Practical Guide for Programmers


/*
    TCPEchoServer
    Original:
    http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html
*/


#include <pthread.h>
/*
スレッド使うときは、ライブラリにpthreadを追加しないと
undefined reference to `pthread_create
とビルドするときに怒られます。
*/
#include <stdio.h>      /* for printf() and fprintf() */

#include <sys/socket.h> /* for socket(), bind(), and connect() */
#include <arpa/inet.h>  /* for sockaddr_in and inet_ntoa() */
#include <stdlib.h>     /* for atoi() and exit() */
#include <string.h>     /* for memset() */
#include <unistd.h>     /* for close() */


void *ThreadMain(void *arg);            /* Main program of a thread */


/* Structure of arguments to pass to client thread */
struct ThreadArgs
{
    int clntSock;                      /* Socket descriptor for client */
};

//エラーメッセージ出して終了する関数
void DieWithError(char *errorMessage)
{
    perror(errorMessage);
    exit(1);
}


//バッファサイズ(byte)
#define RCVBUFSIZE 256   /* Size of receive buffer */


//TCPクライアントに対する応答
void HandleTCPClient(int clntSocket)
{
    char echoBuffer[RCVBUFSIZE];        /* Buffer for echo string */
    int recvMsgSize;                    /* Size of received message */

    //受け取った文字列をそのまま送り返して、こちらからソケットを閉じる。
    //クライアントが1回なにか送ってきたら、そこで通信は終わります。

    /* Receive message from client */
    if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
        DieWithError("recv() failed");

    /* Send received string and receive again until end of transmission */
    while (recvMsgSize > 0)      /* zero indicates end of transmission */
    {
        /* Echo message back to client */
        if (send(clntSocket, echoBuffer, recvMsgSize, 0) != recvMsgSize)
            DieWithError("send() failed");

        /* See if there is more data to receive */
        if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
            DieWithError("recv() failed");
    }

    close(clntSocket);    /* Close client socket */
}


//待ち行列に待たせておく数
#define MAXPENDING 5    /* Maximum outstanding connection requests */


int CreateTCPServerSocket(unsigned short port)
{
    int sock;                        /* socket to create */
    struct sockaddr_in echoServAddr; /* Local address */

    /* Create socket for incoming connections */
    if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        DieWithError("socket() failed");
      
    /* Construct local address structure */
    memset(&echoServAddr, 0, sizeof(echoServAddr));   /* Zero out structure */
    echoServAddr.sin_family = AF_INET;                /* Internet address family */
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
    echoServAddr.sin_port = htons(port);              /* Local port */

    /* Bind to the local address */
    if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0)
        DieWithError("bind() failed");

    /* Mark the socket so it will listen for incoming connections */
    if (listen(sock, MAXPENDING) < 0)
        DieWithError("listen() failed");

    return sock;
}

//クライアントが接続してきた時の処理.
int AcceptTCPConnection(int servSock)
{
    int clntSock;                    /* Socket descriptor for client */
    struct sockaddr_in echoClntAddr; /* Client address */
    unsigned int clntLen;            /* Length of client address data structure */

    /* Set the size of the in-out parameter */
    clntLen = sizeof(echoClntAddr);
    
    /* Wait for a client to connect */
    if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, 
           &clntLen)) < 0)
        DieWithError("accept() failed");
    
    /* clntSock is connected to a client! */
    
    printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));

    return clntSock;
}


int main(int argc, char *argv[])
{
    int servSock;                    /* Socket descriptor for server */
    int clntSock;                    /* Socket descriptor for client */
    unsigned short echoServPort;     /* Server port */
    pthread_t threadID;              /* Thread ID from pthread_create() */
    struct ThreadArgs *threadArgs;   /* Pointer to argument structure for thread */

    if (argc != 2)     /* Test for correct number of arguments */
    {
        fprintf(stderr,"Usage:  %s <SERVER PORT>\n", argv[0]);
       echoServPort=80; 
        //exit(1);
    }
    else{

    echoServPort = atoi(argv[1]);  /* First arg:  local port */
}
    servSock = CreateTCPServerSocket(echoServPort);

    for (;;) /* run forever */
    {
    clntSock = AcceptTCPConnection(servSock);

        /* Create separate memory for client argument */
        if ((threadArgs = (struct ThreadArgs *) malloc(sizeof(struct ThreadArgs))) 
               == NULL)
            DieWithError("malloc() failed");
        threadArgs -> clntSock = clntSock;

        /* Create client thread */
        if (pthread_create(&threadID, NULL, ThreadMain, (void *) threadArgs) != 0)
            DieWithError("pthread_create() failed");
        printf("with thread %ld\n", (long int) threadID);
    }
    /* NOT REACHED */
}

void *ThreadMain(void *threadArgs)
{
    int clntSock;                   /* Socket descriptor for client connection */

    /* Guarantees that thread resources are deallocated upon return */
     pthread_detach(pthread_self()); 

    /* Extract socket file descriptor from argument */
    clntSock = ((struct ThreadArgs *) threadArgs) -> clntSock;
    free(threadArgs);              /* Deallocate memory for argument */

    HandleTCPClient(clntSock);

    return (NULL);
}


これでechoサーバーを作ることができたので、あとはこのソース内のHandleTCPClient部分をいじれば、カメラの画像をクライアントに送りつけることもできるようになると思います。

ではまた。

0 件のコメント:

コメントを投稿