Linuxの下でsocket通信の究極のガイドライン(TCP、UDPの完全なコードを添付)

14975 ワード

linuxの下でsocket通信を使って、TCP、UDPの2種類のプロトコルがあって、ネット上の多くのチュートリアルは2つを混ぜて、あるいはその中の1つだけを話します.今私は自分のこの2,3日の研究の成果をまとめて、1つの完全なことを書いて、初心者の参考に適して、自分で後で調べるのも便利です.
まずsocketとは何かを話して、理論が好きではないのは省略することができます.
Berkeleyソケットアプリケーションインタフェース(API)は、C言語で書かれたアプリケーション開発ライブラリを含み、主にプロセス間通信を実現するために使用され、コンピュータネットワーク通信の面で広く使用されている.(wikipedia socketより)
次に、一般的なsocket API(wikipedia socketからも)について説明します.
 
このリストは、BerkeleyソケットAPIライブラリが提供する関数またはメソッドの概要です.
  • socket()は、システムリソースを割り当てる整数値で識別された新しい決定タイプのソケットを作成します.
  • bind()は、一般にサーバ側で使用され、1つのソケットを1つのソケットアドレス構造、例えば、指定されたローカルポートおよびIPアドレスに関連付ける.
  • listen()は、サーバ側で使用され、バインドされたTCPソケットをリスニング状態にする.
  • connect()は、クライアントに使用され、1つのソケットに自由なローカルポート番号を割り当てる.TCPソケットであれば、新しいTCP接続を取得しようとします.
  • accept()は、サーバ側で使用される.リモートクライアントから新しいTCP接続の作成を要求されたアクセス要求を受け入れ、接続に対応するソケットアドレスに関連付けられた新しいソケットを作成します.
  • send()およびrecv()、またはwrite()およびread()、またはrecvfrom()およびsendto()は、リモートソケットからデータを送信および受信するために使用される.
  • close()は、システムがソケットに割り当てられたリソースを解放するために使用される.TCPの場合、接続が中断されます.
  • gethostbyname()およびgethostbyaddr()は、ホスト名およびアドレスを解析するために使用される.
  • select()は、読む準備、書く準備、またはエラーがある場合のソケットリストを修正するために使用されます.
  • poll()は、ソケットの状態をチェックするために使用される.ソケットは、書き込み、読み取り、またはエラーがあるかどうかをテストできます.
  • getsockopt()は、指定されたソケットの特定のソケットオプションの現在の値を問い合わせるために使用されます.
  • setsockopt()は、指定されたソケットに対して特定のソケットオプションを設定するために使用される.

  • 詳細は以下の通りです.

    [編集]socket()

    socket()は、通信のためにエンドポイントを作成し、ソケットにファイル記述子を返します.socket()には3つのパラメータがあります.
  • domainは、作成されたソケットにプロトコルセットを指定します.例:
  • PF_INETはIPv 4ネットワークプロトコル
  • を表す.
  • PF_INET6はIPv 6
  • を表す.
  • PF_UNIXローカルソケット(1ファイル使用)
  • を表す.
  • typeは以下の通りです.
  • SOCK_STREAM(信頼できるストリーミングサービスまたはストリーミングソケット向け)
  • .
  • SOCK_DGRAM(データメッセージサービスまたはデータメッセージソケット)
  • SOCK_SEQPACKET(信頼性の高い連続パケットサービス)
  • SOCK_RAW(ネットワーク層上の元のプロトコル).

  • protocolは、実際に使用される転送プロトコルを指定します.最も一般的なのはIPPROTO_TCPIPPROTO_SCTPIPPROTO_UDPIPPROTO_DCCPです.これらのプロトコルはに詳細な説明があります.この項目が「0」の場合、選択したdomainおよびtypeに基づいてデフォルトプロトコルの使用を選択します.

  • エラーが発生した場合、関数の戻り値は-1です.そうでない場合、関数は新しく割り当てられた記述子を表す整数を返します.
    プロトタイプ:
    int socket(int domain, int type, int protocol)

    [編集]bind()

    bind()は、ソケットにアドレスを割り当てます.socket()を使用してソケットを作成すると、使用するプロトコルのみが付与され、アドレスは割り当てられません.他のホストの接続を受け入れる前にbind()を呼び出してソケットにアドレスを割り当てる必要があります.bind()には3つのパラメータがあります.
  • sockfdはbind関数を用いるソケット記述子
  • を表す.
  • my_addrは、sockaddr構造(割り当てられたアドレスを表す)へのポインタ
  • を指す.
  • addrlen、socklen_でtフィールドはsockaddr構造の長さ
  • を指定する
    エラーが発生した場合、関数の戻り値は-1、そうでない場合は0です.
    プロトタイプ
    int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
    
    

    [編集]listen()


    SOcketがアドレスにバインドされると、listen()関数は、可能な接続要求の傍受を開始する.しかしながら、これは、信頼性の高いデータストリーム保証がある場合にのみ使用され、例えば、データ型(SOCK_STREAMSOCK_SEQPACKET)である.
    Listen()関数には2つのパラメータが必要です.
  • sockfd、socketの記述子.
  • backlogは、リスニングキューのサイズを決定する整数であり、接続要求が来ると、このリスニングキューに入り、キューがいっぱいになると、新しい接続要求がエラーを返す.リクエストが受け入れられると、0が返されます.逆に、エラーは-1を返します.

  • プロトタイプ:
    int listen(int sockfd, int backlog);
    
    

    [編集]accept()


    アプリケーションが他のホストからのデータ・ストリームへの接続をリスニングすると、Unix select()システム呼び出しなどのイベントによって通知されます.接続はaccept()関数で初期化する必要があります.Accept()は、各接続に新しいソケットを作成し、この接続をリスニングキューから削除します.次のパラメータを使用します.
  • sockfd、傍受ソケット記述子
  • cliaddr、sockaddr構造体へのポインタ、クライアントアドレス情報.
  • addrlenは、socklen_tのポインタを指し、クライアントアドレス構造体の大きさを決定する.

  • 新しいソケット記述子を返し、エラーは-1を返します.さらなる通信はこのソケットを通じて行わなければならない.
    Datagramソケットはaccept()で処理する必要はありません.受信者がこの要求をリスニングソケットで直ちに処理する可能性があるためです.
    関数のプロトタイプ:
    int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
    
    

    [編集]connect()

    connect()システム呼び出しは、ファイル記述子とホストアドレスのパラメータを持つソケット設定接続である.
    いくつかのタイプのソケットは接続されていません.ほとんどはUDPプロトコルです.これらのソケットについては、デフォルトでデータを送信および受信するホストが所与のアドレスによって決定され、send()およびrecv()を使用することができる.-1を返すとエラーが発生し、0は成功を示します.
    関数のプロトタイプ:
    int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

     
    TCPソケット通信
    サーバ側のプロセスは次のとおりです.
    1.serverSocketの作成
    2.serverAddrの初期化(サーバアドレス)
    3.socketとserverAddrをbindにバインドする
    4.リスニング開始
    5.whileサイクルに入り、acceptアクセスのクライアントsocketを継続し、writeとreadの読み書き操作を行う
    6.serverSocketを閉じる
    クライアントプロセス:
    1.クライアントソケットの作成
    2.serverAddrの初期化
    3.サーバ接続へのリンク
    4.writeとreadによる読み書き操作
    5.クライアントソケットを閉じる
    具体的な実装コードは以下の通りです.
     
    #server.c(TCP)
    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <sys/types.h>
    
    #include <sys/socket.h>
    
    #include <netinet/in.h>
    
    #include <errno.h>
    
    #define SRVPORT 10005
    
    #define CONNECT_NUM 5
    
    #define MAX_NUM 80
    
    int main()
    
    {
    
        int serverSock=-1,clientSock=-1;
    
        struct sockaddr_in serverAddr;
    
    
    
        serverSock=socket(AF_INET,SOCK_STREAM,0);
    
        if(serverSock<0)
    
        {
    
            printf("socket creation failed
    "); exit(-1); } printf("socket create successfully.
    "); memset(&serverAddr,0,sizeof(serverAddr)); serverAddr.sin_family=AF_INET; serverAddr.sin_port=htons((u_short) SRVPORT); serverAddr.sin_addr.s_addr=htons(INADDR_ANY); if(bind(serverSock,&serverAddr,sizeof(struct sockaddr_in))==-1) { printf("Bind error.
    "); exit(-1); } printf("Bind successful.
    "); if(listen(serverSock,10)==-1) { printf("Listen error!
    "); } printf("Start to listen!
    "); char revBuf[MAX_NUM]={0}; char sedBuf[MAX_NUM]={0}; while(1) { clientSock=accept(serverSock,NULL,NULL); while(1) { if(read(clientSock,revBuf,MAX_NUM)==-1) { printf("read error.
    "); } else { printf("Client:%s
    ",revBuf); } if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0) { strcpy(sedBuf,"Goodbye,my dear client!"); } else { strcpy(sedBuf,"Hello Client."); } if(write(clientSock,sedBuf,sizeof(sedBuf))==-1) { printf("Send error!
    "); } printf("Me(Server):%s
    ",sedBuf); if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0) { break; } bzero(revBuf,sizeof(revBuf)); bzero(sedBuf,sizeof(sedBuf)); } close(clientSock); } close(serverSock); return 0; }
    #client.c(TCP)
    
    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <sys/types.h>
    
    #include <sys/socket.h>
    
    #include <netinet/in.h>
    
    #include <errno.h>
    
    #include <string.h>
    
    #define SRVPORT 10005
    
    #define CONNECT_NUM 5
    
    #define MAX_NUM 80
    
    int main()
    
    {
    
        int clientSock=-1;
    
        struct sockaddr_in serverAddr;
    
    
    
        clientSock=socket(AF_INET,SOCK_STREAM,0);
    
        if(clientSock<0)
    
        {
    
            printf("socket creation failed
    "); exit(-1); } printf("socket create successfully.
    "); memset(&serverAddr,0,sizeof(serverAddr)); serverAddr.sin_family=AF_INET; serverAddr.sin_port=htons((u_short) SRVPORT); serverAddr.sin_addr.s_addr=htons(INADDR_ANY); if(connect(clientSock,&serverAddr,sizeof(struct sockaddr_in))<0) { printf("Connect error.
    "); exit(-1); } printf("Connect successful.
    "); char sedBuf[MAX_NUM]={0}; char revBuf[MAX_NUM]={0}; while(gets(sedBuf)!=-1) { if(write(clientSock,sedBuf,strlen(sedBuf))==-1) { printf("send error!
    "); } printf("Me(Client):%s
    ",sedBuf); bzero(sedBuf,sizeof(sedBuf)); if(read(clientSock,revBuf,MAX_NUM)==-1) { printf("rev error!
    "); } printf("Sever:%s
    ",revBuf); if(strcmp(revBuf,"Goodbye,my dear client!")==0) break; bzero(revBuf,sizeof(revBuf)); } close(clientSock); return 0; }

    UDPプロトコルはデータ通信の信頼性を保証できないが、コストが低く、編成も簡単である.
     
    サーバプロセス:
    1.serverSocketの作成
    2.サーバアドレスserverAddrの設定
    3.serverSocketとserverAddrをbindにバインドする
    4.読み書きsendtoとrecvfromの開始
    5.serverSocketを閉じる
    クライアントプロセス
    1.クライアントソケットの作成
    2.サーバアドレスserverAddrの設定
    3.オプションでclientAddrとclientSocketを設定します(一般的にはバインドしません)
    4.送信操作sendto
    5.クライアントソケットを閉じる
    具体的なコードは以下の通りです.
     
    #server.c(UDP)
    
    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <unistd.h>
    
    #include <netinet/in.h>//for sockaddr_in
    
    #include <arpa/inet.h>//for socket
    
    int main()
    
    {
    
     int fd=socket(AF_INET,SOCK_DGRAM,0);
    
     if(fd==-1)
    
     {
    
     perror("socket create error!
    "); exit(-1); } printf("socket fd=%d
    ",fd); struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons(6666); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); int r; r=bind(fd,(struct sockaddr*)&addr,sizeof(addr)); if(r==-1) { printf("Bind error!
    "); close(fd); exit(-1); } printf("Bind successfully.
    "); char buf[255]; struct sockaddr_in from; socklen_t len; len=sizeof(from); while(1) { r=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&from,&len); if(r>0) { buf[r]=0; printf("The message received for %s is :%s
    ",inet_ntoa(from.sin_addr),buf); } else { break; } } close(fd); return 0; }

     
    #client.c(UDP)
    
    #include <stdio.h>
    
    #include <stdlib.h>
    
    #include <unistd.h>
    
    #include <netinet/in.h>//for sockaddr_in
    
    #include <arpa/inet.h>//for socket 
    
    int main()
    
    {
    
     int fd=socket(AF_INET,SOCK_DGRAM,0);
    
     if(fd==-1)
    
     {
    
     perror("socket create error!
    "); exit(-1); } printf("socket fd=%d
    ",fd); struct sockaddr_in addr_to;// addr_to.sin_family=AF_INET; addr_to.sin_port=htons(6666); addr_to.sin_addr.s_addr=inet_addr("127.0.0.1"); struct sockaddr_in addr_from; addr_from.sin_family=AF_INET; addr_from.sin_port=htons(0);// addr_from.sin_addr.s_addr=htons(INADDR_ANY);// r=bind(fd,(struct sockaddr*)&addr_from,sizeof(addr_from)); int r; if(r==-1) { printf("Bind error!
    "); close(fd); exit(-1); } printf("Bind successfully.
    "); char buf[255]; int len; while(1) { r=read(0,buf,sizeof(buf)); if(r<0) { break; } len=sendto(fd,buf,r,0,(struct sockaddr*)&addr_to,sizeof(addr_to)); if(len==-1) { printf("send falure!
    "); } else { printf("%d bytes have been sended successfully!
    ",len); } } close(fd); return 0; }

    以上のコードはすべてテストされ(Ubuntu 12.04)、実行できます.疑問があればladdにメールしてもいいです[email protected]
     
    参考記事
    1.wikipedia socket  http://zh.wikipedia.org/wiki/Socket
    2.TCPソケットのlinux実現http://os.51cto.com/art/201001/179878.htm
    本文はladdオリジナルで、転載を歓迎しますが、出典を明記してください.
    http://www.cnblogs.com/ladd/archive/2012/06/25/2560888.html