web-dev-qa-db-ja.com

ソケットconnect()vs bind()

connect()システム呼び出しとbind()システム呼び出しは両方とも、ソケットファイル記述子をアドレス(通常はip/portの組み合わせ)に「関連付け」ます。プロトタイプは次のようなものです:-

int connect(int sockfd, const struct sockaddr *addr,
               socklen_t addrlen);

そして

int bind(int sockfd, const struct sockaddr *addr,
            socklen_t addrlen);

2つの呼び出しの正確な違いは何ですか? connect()をいつ使用するか、およびbind()を使用するのはいつですか?

具体的には、いくつかのサンプルサーバークライアントコードで、クライアントがconnect()を使用しており、サーバーがbind()呼び出しを使用していることがわかりました。理由は私には完全には明らかではありませんでした。

97

理解を深めるために、正確にバインドと接続が行われる場所を見つけましょう。

Souravが明らかにしたように、2つの呼び出しの位置付けに加えて、

bind()はソケットをそのローカルアドレスに関連付けます[そのため、サーバー側がバインドするため、クライアントはそのアドレスを使用してサーバーに接続できます。] connect()を使用してリモート[サーバー]アドレスに接続します。 、接続[読み取り:サーバーに接続]が使用されます。

特定の役割と対応する実装のために、同じマシン上にクライアント/サーバーがある場合でも、それらを交換して使用することはできません。

さらに、これらの呼び出しTCP/IPハンドシェイクを相関させることをお勧めします。

enter image description here

したがって、ここでSYNを送信するのは、connect()です。一方、bind()は通信エンドポイントの定義に使用されます。

お役に立てれば!!

183
Jain Rach

1つのライナー:bind()が自分のアドレスに、connect()がリモートアドレスに。

bind() のmanページから引用

bind()は、addrで指定されたアドレスを、ファイル記述子sockfdで参照されるソケットに割り当てます。 addrlenは、addrが指すアドレス構造のサイズをバイト単位で指定します。従来、この操作は「ソケットへの名前の割り当て」と呼ばれています。

そして、 connect() の同じものから

Connect()システムコールは、ファイル記述子sockfdによって参照されるソケットをaddrで指定されたアドレスに接続します。

明確にするために、

  • bind()はソケットをローカルアドレスに関連付けます[そのため、サーバー側のbindsを使用して、クライアントがそのアドレスを使用してサーバーに接続できるようにします。]
  • connect()はリモート[サーバー]アドレスに接続するために使用されます。そのため、クライアント側ではconnect [読み取り:サーバーに接続]が使用されます。
38
Sourav Ghosh

ウィキペディアから http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

connect():

Connect()システムコールは、ファイル記述子によって識別されるソケットを、引数リスト内のそのホストのアドレスによって指定されたリモートホストに接続します。

特定のタイプのソケットはコネクションレスであり、最も一般的なのはユーザーデータグラムプロトコルソケットです。これらのソケットの場合、connectは特別な意味を持ちます。データを送受信するためのデフォルトのターゲットは指定されたアドレスに設定され、コネクションレスソケットでsend()やrecv()などの関数を使用できます。

connect()は、エラーコードを表す整数を返します。0は成功を表し、-1はエラーを表します。

bind():

bind()はソケットをアドレスに割り当てます。 socket()を使用してソケットを作成すると、プロトコルファミリのみが与えられ、アドレスは割り当てられません。このアドレスとの関連付けは、ソケットが他のホストへの接続を受け入れる前に、bind()システムコールで実行する必要があります。 bind()は3つの引数を取ります:

sockfd、バインドを実行するソケットを表す記述子。 my_addr、バインド先のアドレスを表すsockaddr構造体へのポインター。 addrlen、sockaddr構造体のサイズを指定するsocklen_tフィールド。 Bind()は、成功すると0を返し、エラーが発生すると-1を返します。

例:1.)Connectの使用

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(){
  int clientSocket;
  char buffer[1024];
  struct sockaddr_in serverAddr;
  socklen_t addr_size;

  /*---- Create the socket. The three arguments are: ----*/
  /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
  clientSocket = socket(PF_INET, SOCK_STREAM, 0);

  /*---- Configure settings of the server address struct ----*/
  /* Address family = Internet */
  serverAddr.sin_family = AF_INET;
  /* Set port number, using htons function to use proper byte order */
  serverAddr.sin_port = htons(7891);
  /* Set the IP address to desired Host to connect to */
  serverAddr.sin_addr.s_addr = inet_addr("192.168.1.17");
  /* Set all bits of the padding field to 0 */
  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

  /*---- Connect the socket to the server using the address struct ----*/
  addr_size = sizeof serverAddr;
  connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);

  /*---- Read the message from the server into the buffer ----*/
  recv(clientSocket, buffer, 1024, 0);

  /*---- Print the received message ----*/
  printf("Data received: %s",buffer);   

  return 0;
}

2.)バインドの例:

int main()
{
    struct sockaddr_in source, destination = {};  //two sockets declared as previously
    int sock = 0;
    int datalen = 0;
    int pkt = 0;

    uint8_t *send_buffer, *recv_buffer;

    struct sockaddr_storage fromAddr;   // same as the previous entity struct sockaddr_storage serverStorage;
    unsigned int addrlen;  //in the previous example socklen_t addr_size;
    struct timeval tv;
    tv.tv_sec = 3;  /* 3 Seconds Time-out */
    tv.tv_usec = 0;

    /* creating the socket */         
    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 
        printf("Failed to create socket\n");

    /*set the socket options*/
    setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval));

    /*Inititalize source to zero*/
    memset(&source, 0, sizeof(source));       //source is an instance of sockaddr_in. Initialization to zero
    /*Inititalize destinaton to zero*/
    memset(&destination, 0, sizeof(destination));


    /*---- Configure settings of the source address struct, WHERE THE PACKET IS COMING FROM ----*/
    /* Address family = Internet */
    source.sin_family = AF_INET;    
    /* Set IP address to localhost */   
    source.sin_addr.s_addr = INADDR_ANY;  //INADDR_ANY = 0.0.0.0
    /* Set port number, using htons function to use proper byte order */
    source.sin_port = htons(7005); 
    /* Set all bits of the padding field to 0 */
    memset(source.sin_zero, '\0', sizeof source.sin_zero); //optional


    /*bind socket to the source WHERE THE PACKET IS COMING FROM*/
    if (bind(sock, (struct sockaddr *) &source, sizeof(source)) < 0) 
        printf("Failed to bind socket");

    /* setting the destination, i.e our OWN IP ADDRESS AND PORT */
    destination.sin_family = AF_INET;                 
    destination.sin_addr.s_addr = inet_addr("127.0.0.1");  
    destination.sin_port = htons(7005); 

    //Creating a Buffer;
    send_buffer=(uint8_t *) malloc(350);
    recv_buffer=(uint8_t *) malloc(250);

    addrlen=sizeof(fromAddr);

    memset((void *) recv_buffer, 0, 250);
    memset((void *) send_buffer, 0, 350);

    sendto(sock, send_buffer, 20, 0,(struct sockaddr *) &destination, sizeof(destination));

    pkt=recvfrom(sock, recv_buffer, 98,0,(struct sockaddr *)&destination, &addrlen);
    if(pkt > 0)
        printf("%u bytes received\n", pkt);
    }

違いが明らかになることを願っています

宣言するソケットタイプは必要なものに依存することに注意してください。これは非常に重要です

7
Khan

bindは、実行中のプロセスにポートを要求するように指示します。つまり、ポート80に自身をバインドし、着信リクエストをリッスンする必要があります。バインドを使用すると、プロセスがサーバーになります。 connectを使用する場合、すでに使用中のポートに接続するようにプロセスに指示します。プロセスがクライアントになります。違いは重要です:bindは使用されていないポートを要求し(そのためそれを要求してサーバーになることができます)、connectは既に使用されているポートを要求します(したがって接続してサーバーと通信できるようにします)

6
Philipp Murry

connect()listen()ではなく、connect()bind()を対応物と考えると、理解に役立つと思います。これは、bind()の前に呼び出すか、connect()の前に呼び出さないことはめったにありませんが、listen()を呼び出すか省略することができるためです。

サーバーとクライアントの観点から考えると、前者の特徴であるlisten()と後者のconnect()です。 bind()はどちらでも見つかります-または見つかりません-。

サーバーとクライアントが異なるマシン上にあると仮定すると、さまざまな機能を理解しやすくなります。

bind()はローカルで動作します。つまり、呼び出されたマシン上の接続の終わりを要求されたアドレスにバインドし、要求されたポートをユーザーに割り当てます。そのマシンがクライアントであるかサーバーであるかに関係なく、それを行います。 connect()は、サーバーへの接続を開始します。つまり、クライアントからサーバー上の要求されたアドレスとポートに接続します。 bind()を使用して接続するアドレスとポートを知ることができるように、そのサーバーはlisten()の前にconnect()をほぼ確実に呼び出します。

bind()を呼び出さない場合、connect()(クライアント)またはlisten()(サーバー)を呼び出すと、ポートとアドレスが暗黙的に割り当てられ、ローカルマシンにバインドされます。ただし、それは両方の副作用であり、目的ではありません。この方法で割り当てられたポートは一時的です。

ここで重要な点は、クライアントがサーバーに接続するため、クライアントをバインドする必要がないということです。したがって、特定の何かにバインドするのではなく、一時ポートを使用していても、サーバーはクライアントのアドレスとポートを認識します。一方、サーバーはlisten()を呼び出さずにbind()を呼び出すこともできますが、そのシナリオでは、割り当てられた一時ポートを検出し、それを接続するクライアントに通知する必要があります。

あなたがconnect()に言及しているように、TCPに興味があるが、これはUDPにも引き継がれ、最初のbind()(UDPはコネクションレス)の前にsendto()を呼び出さないと、ポートとアドレスが暗黙的に割り当てられバインドされると仮定します。バインドなしで呼び出すことのできない関数の1つはrecvfrom()です。これは、割り当てられたポートとバインドされたアドレスがないと、受信するものがない(または、バインディングがないことを解釈する方法によっては多すぎる)ため、エラーを返します。

3
pjcard

長すぎる;読まない:違いは、ソース(ローカル)または宛先アドレス/ポートが設定されているかどうかです。つまり、bind()がソースを設定し、connect()が宛先を設定します。 TCPまたはUDPに関係なく。

bind()

bind()は、ソケットのローカル(ソース)アドレスを設定します。これは、パケットが受信されるアドレスです。ソケットによって送信されたパケットは、これを送信元アドレスとして運ぶため、他のホストはパケットをどこに送り返すかを知っています。

受信が不要な場合、ソケットの送信元アドレスは役に立ちません。 TCPなどのプロトコルでは、適切に送信するために受信を有効にする必要があります。宛先ホストは、1つ以上のパケットが到着すると確認を返す(つまり、確認応答)ためです。

connect()

  • TCPには「接続済み」状態があります。 connect()は、TCPコードをトリガーして、反対側への接続を確立しようとします。
  • UDPには「接続」状態はありません。 connect()は、アドレスが指定されていない場合にのみパケットが送信されるデフォルトのアドレスを設定します。 connect()を使用しない場合は、宛先アドレスを含むsendto()またはsendmsg()を使用する必要があります。

connect()または送信関数が呼び出され、アドレスがバインドされていない場合、Linuxはソケットをランダムなポートに自動的にバインドします。技術的な詳細については、Linuxカーネルソースコードの inet_autobind() をご覧ください。

サイドノート

  • listen()はTCPのみです。
  • AF_INETファミリでは、ソケットの送信元または宛先アドレス(struct sockaddr_in)はIPアドレスで構成されます( IP header を参照)、およびTCPまたはUDPポート( TCP および UDP ヘッダーを参照)。