socket kết nối () so với bind ()


121

Cả hai connect()bind() hệ thống đều gọi 'liên kết' bộ mô tả tệp socket với một địa chỉ (thường là kết hợp ip / cổng). Nguyên mẫu của chúng giống như: -

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

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

Sự khác biệt chính xác giữa 2 cuộc gọi là gì? Khi nào thì nên sử dụngconnect() và khi bind()nào?

Cụ thể, trong một số mã máy khách máy chủ mẫu, thấy rằng máy khách đang sử dụng connect()và máy chủ đang sử dụng bind()cuộc gọi. Lý do không hoàn toàn rõ ràng đối với tôi.


19
Trong một câu: bind là địa chỉ cục bộ, kết nối là địa chỉ từ xa.
SHR

Câu trả lời:


230

Để hiểu rõ hơn, chúng ta hãy tìm hiểu xem ràng buộc và kết nối chính xác ở đâu,

Hơn nữa để xác định vị trí của hai cuộc gọi, như được Sourav làm rõ,

bind () liên kết socket với địa chỉ cục bộ của nó [đó là lý do tại sao phía máy chủ liên kết, để máy khách có thể sử dụng địa chỉ đó để kết nối với máy chủ.] connect () được sử dụng để kết nối với địa chỉ [máy chủ] từ xa, đó là lý do tại sao phía máy khách , kết nối [đọc là: kết nối với máy chủ] được sử dụng.

Chúng tôi không thể sử dụng chúng thay thế cho nhau (ngay cả khi chúng tôi có máy khách / máy chủ trên cùng một máy) vì các vai trò cụ thể và cách triển khai tương ứng.

Tôi sẽ đề xuất thêm để tương quan với các cuộc gọi bắt tay TCP / IP này.

nhập mô tả hình ảnh ở đây

Vì vậy, ai sẽ gửi SYN ở đây, nó sẽ được kết nối (). Trong khi bind () được sử dụng để xác định điểm kết thúc giao tiếp.

Hi vọng điêu nay co ich!!


1
cảm ơn nhé người anh em. Với sơ đồ, mọi thứ có thể xáo trộn nhanh chóng. Bạn có thể cho biết sự khác biệt ở đây là gì, Nếu chúng ta đang sử dụng udp?
apm

8
accept () <br> nên được chuyển xuống dưới <br> khối cho đến khi có kết nối từ máy khách
tschodt 30/09/15

tôi nghĩ rằng tất cả các nút trong mạng trong mạng p2p nên sử dụng liên kết, tôi có đúng không?
kapil

46

Lớp lót duy nhất: bind() đến địa chỉ riêng, connect()đến địa chỉ từ xa.

Trích dẫn từ trang người đàn ông của bind()

bind () gán địa chỉ được chỉ định bởi addr cho socket được tham chiếu bởi bộ mô tả tệp sockfd. addrlen chỉ định kích thước, tính bằng byte, của cấu trúc địa chỉ được trỏ tới bởi addr. Theo truyền thống, thao tác này được gọi là "gán tên cho một ổ cắm".

và, từ cùng một cho connect()

Lệnh gọi hệ thống connect () kết nối socket được sockfd bộ mô tả tệp tham chiếu đến địa chỉ được chỉ định bởi addr.

Làm rõ,

  • bind()liên kết socket với địa chỉ cục bộ của nó [đó là lý do tại sao phía máy chủ bind, để máy khách có thể sử dụng địa chỉ đó để kết nối với máy chủ.]
  • connect() được sử dụng để kết nối với địa chỉ [máy chủ] từ xa, đó là lý do tại sao phía máy khách, kết nối [đọc là: kết nối với máy chủ] được sử dụng.

Vì vậy, giả sử, nếu cả hai tiến trình máy chủ và máy khách chạy trên cùng một máy, chúng có thể được sử dụng thay thế cho nhau không?
Siddhartha Ghosh

1
@SiddharthaGhosh Không. Có thể máy khách và máy chủ nằm trên cùng một máy, nhưng chúng vẫn quá trình khác nhau, phải không? Cả hai API đều phục vụ con đường của chính nó. Họ không bao giờinterchangeable
Sourav Ghosh

Chính xác thì cục bộ và từ xa có nghĩa là gì trong ngữ cảnh này?
Siddhartha Ghosh

@SiddharthaGhosh local-> chính quy trình, remote-> quy trình khác.
Sourav Ghosh

@SouravGhosh vì vậy điều này có nghĩa là tôi không thể chỉ định một cổng để liên kết ở phía máy khách?
Hengqi Chen

12

ràng buộc cho biết quá trình đang chạy để xác nhận một cổng. tức là, nó sẽ tự liên kết với cổng 80 và lắng nghe các yêu cầu gửi đến. với ràng buộc, quy trình của bạn trở thành một máy chủ. khi bạn sử dụng kết nối, bạn yêu cầu quy trình của bạn kết nối với một cổng ĐÃ ĐƯỢC sử dụng. quy trình của bạn trở thành khách hàng. sự khác biệt rất quan trọng: bind muốn một cổng không được sử dụng (để nó có thể xác nhận nó và trở thành một máy chủ) và kết nối muốn một cổng đã được sử dụng (để nó có thể kết nối với nó và nói chuyện với máy chủ)


9

Từ Wikipedia http://en.wikipedia.org/wiki/Berkeley_sockets#bind.28.29

kết nối():

Lệnh gọi hệ thống connect () kết nối một ổ cắm, được xác định bởi bộ mô tả tệp của nó, với một máy chủ từ xa được chỉ định bởi địa chỉ của máy chủ đó trong danh sách đối số.

Một số loại ổ cắm không có kết nối, thông thường nhất là ổ cắm giao thức datagram của người dùng. Đối với các ổ cắm này, kết nối có một ý nghĩa đặc biệt: mục tiêu mặc định để gửi và nhận dữ liệu được đặt thành địa chỉ đã cho, cho phép sử dụng các hàm như send () và recv () trên các ổ cắm không kết nối.

connect () trả về một số nguyên đại diện cho mã lỗi: 0 đại diện cho thành công, trong khi -1 đại diện cho một lỗi.

trói buộc():

bind () gán một socket cho một địa chỉ. Khi một ổ cắm được tạo bằng socket (), nó chỉ được cấp một họ giao thức, nhưng không được gán một địa chỉ. Việc liên kết với một địa chỉ này phải được thực hiện với lệnh gọi hệ thống bind () trước khi socket có thể chấp nhận kết nối với các máy chủ khác. bind () có ba đối số:

sockfd, một bộ mô tả đại diện cho socket để thực hiện liên kết. my_addr, một con trỏ đến cấu trúc sockaddr đại diện cho địa chỉ để liên kết. addrlen, một trường socklen_t xác định kích thước của cấu trúc sockaddr. Bind () trả về 0 khi thành công và -1 nếu xảy ra lỗi.

Ví dụ: 1.) Sử dụng 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.) Ví dụ ràng buộc:

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);
    }

Tôi hy vọng điều đó làm rõ sự khác biệt

Xin lưu ý rằng loại ổ cắm mà bạn khai báo sẽ phụ thuộc vào những gì bạn yêu cầu, điều này cực kỳ quan trọng


9

Tôi nghĩ rằng nó sẽ giúp ích cho sự hiểu biết của bạn nếu bạn nghĩ về connect()listen()như những người đối tác, hơn là connect()bind(). Lý do cho điều này là bạn có thể gọi hoặc bỏ qua bind()một trong hai, mặc dù hiếm khi gọi trước connect()hoặc không gọi trướclisten() .

Nếu suy nghĩ về máy chủ và máy khách, listen()đó là dấu hiệu của cái trước và connect()cái sau.bind()có thể được tìm thấy - hoặc không tìm thấy - trên cả hai.

Nếu giả sử máy chủ và máy khách của chúng ta ở trên các máy khác nhau, thì việc hiểu các chức năng khác nhau sẽ trở nên dễ dàng hơn.

bind()hoạt động cục bộ, nghĩa là nó liên kết phần cuối của kết nối trên máy mà nó được gọi, với địa chỉ được yêu cầu và gán cổng được yêu cầu cho bạn. Nó thực hiện điều đó bất kể máy đó sẽ là máy khách hay máy chủ. connect()khởi tạo kết nối tới máy chủ, có nghĩa là nó kết nối với địa chỉ và cổng được yêu cầu trên máy chủ, từ một máy khách. Máy chủ đó gần như chắc chắn sẽ được gọi bind()trước listen(), để bạn có thể biết địa chỉ và cổng nào để kết nối với nó bằng cách sử dụng connect().

Nếu bạn không gọi bind(), một cổng và địa chỉ sẽ được gán và ràng buộc ngầm trên máy cục bộ cho bạn khi bạn gọi connect()(máy khách) hoặclisten() (máy chủ). Tuy nhiên, đó là tác dụng phụ của cả hai, không phải mục đích của chúng. Một cổng được chỉ định theo cách này là tạm thời.

Một điểm quan trọng ở đây là máy khách không cần phải bị ràng buộc, vì máy khách kết nối với máy chủ và do đó máy chủ sẽ biết địa chỉ và cổng của máy khách mặc dù bạn đang sử dụng một cổng tạm thời, thay vì ràng buộc với một cái gì đó cụ thể. Mặt khác, mặc dù máy chủ có thể gọi listen()mà không cần gọi bind(), trong trường hợp đó, họ sẽ cần phải khám phá ra cổng tạm thời được chỉ định của mình và truyền đạt cổng đó cho bất kỳ máy khách nào muốn kết nối với nó.

Tôi giả sử khi bạn đề cập connect()rằng bạn quan tâm đến TCP, nhưng điều này cũng chuyển sang UDP, nơi việc không gọi bind()trước cái đầu tiên sendto()(UDP là kết nối ít) cũng khiến một cổng và địa chỉ được gán và ràng buộc ngầm. Một hàm bạn không thể gọi mà không có ràng buộc là recvfrom(), hàm này sẽ trả về lỗi, bởi vì không có cổng được chỉ định và địa chỉ ràng buộc, không có gì để nhận từ (hoặc quá nhiều, tùy thuộc vào cách bạn giải thích việc không có ràng buộc).


1

Quá lâu; Không đọc: Sự khác biệt là nguồn (cục bộ) hay địa chỉ / cổng đích đang được đặt. Nói tóm lại, bind()đặt nguồn và connect()đặt đích. Bất kể TCP hay UDP.

bind()

bind()đặt địa chỉ cục bộ (nguồn) của ổ cắm. Đây là địa chỉ nơi các gói được nhận. Các gói được gửi bởi socket mang địa chỉ này làm địa chỉ nguồn, vì vậy máy chủ khác sẽ biết nơi gửi lại các gói của nó.

Nếu không cần nhận, địa chỉ nguồn ổ cắm là vô dụng. Các giao thức như TCP yêu cầu kích hoạt nhận để gửi đúng cách, vì máy chủ đích sẽ gửi lại xác nhận khi một hoặc nhiều gói đã đến (tức là xác nhận).

connect()

  • TCP có trạng thái "đã kết nối". connect()kích hoạt mã TCP để cố gắng thiết lập kết nối với phía bên kia.
  • UDP không có trạng thái "kết nối". connect()chỉ đặt một địa chỉ mặc định để gửi gói tin khi không có địa chỉ nào được chỉ định. Khi nào connect()không được sử dụng, sendto()hoặc sendmsg()phải được sử dụng có chứa địa chỉ đích.

Khi connect()hoặc một hàm gửi được gọi, và không có địa chỉ nào bị ràng buộc, Linux sẽ tự động liên kết socket với một cổng ngẫu nhiên. Để biết chi tiết kỹ thuật, hãy xeminet_autobind() trong mã nguồn nhân Linux.

Ghi chú bên lề

  • listen() chỉ là TCP.
  • Trong họ AF_INET , địa chỉ nguồn hoặc đích của ổ cắm ( struct sockaddr_in) được cấu tạo bởi địa chỉ IP (xem tiêu đề IP ) và cổng TCP hoặc UDP (xem tiêu đề TCPUDP ).
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.