日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

bind()和connect()函数:绑定套接字并建立连接

發布時間:2025/3/12 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 bind()和connect()函数:绑定套接字并建立连接 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

socket() 函數用來創建套接字,確定套接字的各種屬性,然后服務器端要用 bind() 函數將套接字與特定的 IP 地址和端口綁定起來,只有這樣,流經該 IP 地址和端口的數據才能交給套接字處理。類似地,客戶端也要用 connect() 函數建立連接。

bind() 函數

bind() 函數的原型為:

int bind(int sock, struct sockaddr *addr, socklen_t addrlen); //Linux int bind(SOCKET sock, const struct sockaddr *addr, int addrlen); //Windows 下面以 Linux 為例進行講解,Windows 與此類似。

sock 為 socket 文件描述符,addr 為 sockaddr 結構體變量的指針,addrlen 為 addr 變量的大小,可由 sizeof() 計算得出。

下面的代碼,將創建的套接字與IP地址 127.0.0.1、端口 1234 綁定:

?
  • //創建套接字
  • int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  • ?
  • //創建sockaddr_in結構體變量
  • struct sockaddr_in serv_addr;
  • memset(&serv_addr, 0, sizeof(serv_addr)); //每個字節都用0填充
  • serv_addr.sin_family = AF_INET; //使用IPv4地址
  • serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具體的IP地址
  • serv_addr.sin_port = htons(1234); //端口
  • ?
  • //將套接字和IP、端口綁定
  • bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
  • 這里我們使用 sockaddr_in 結構體,然后再強制轉換為 sockaddr 類型,后邊會講解為什么這樣做。

    sockaddr_in 結構體

    接下來不妨先看一下 sockaddr_in 結構體,它的成員變量如下:

    ?
  • struct sockaddr_in{
  • sa_family_t sin_family; //地址族(Address Family),也就是地址類型
  • uint16_t sin_port; //16位的端口號
  • struct in_addr sin_addr; //32位IP地址
  • char sin_zero[8]; //不使用,一般用0填充
  • };
  • 1) sin_family 和 socket() 的第一個參數的含義相同,取值也要保持一致。

    2) sin_prot 為端口號。uint16_t 的長度為兩個字節,理論上端口號的取值范圍為 0~65536,但 0~1023 的端口一般由系統分配給特定的服務程序,例如 Web 服務的端口號為 80,FTP 服務的端口號為 21,所以我們的程序要盡量在 1024~65536 之間分配端口號。

    端口號需要用 htons() 函數轉換,后面會講解為什么。

    3) sin_addr 是 struct in_addr 結構體類型的變量,下面會詳細講解。

    4) sin_zero[8] 是多余的8個字節,沒有用,一般使用 memset() 函數填充為 0。上面的代碼中,先用 memset() 將結構體的全部字節填充為 0,再給前3個成員賦值,剩下的 sin_zero 自然就是 0 了。

    in_addr 結構體

    sockaddr_in 的第3個成員是 in_addr 類型的結構體,該結構體只包含一個成員,如下所示:

    ?
  • struct in_addr{
  • in_addr_t s_addr; //32位的IP地址
  • };
  • in_addr_t 在頭文件 <netinet/in.h> 中定義,等價于 unsigned long,長度為4個字節。也就是說,s_addr 是一個整數,而IP地址是一個字符串,所以需要?inet_addr() 函數進行轉換,例如:

    ?
  • unsigned long ip = inet_addr("127.0.0.1");
  • printf("%ld\n", ip);
  • 運行結果:
    16777343


    圖解 sockaddr_in 結構體


    為什么要搞這么復雜,結構體中嵌套結構體,而不用 sockaddr_in 的一個成員變量來指明IP地址呢?socket() 函數的第一個參數已經指明了地址類型,為什么在 sockaddr_in 結構體中還要再說明一次呢,這不是啰嗦嗎?

    這些繁瑣的細節確實給初學者帶來了一定的障礙,我想,這或許是歷史原因吧,后面的接口總要兼容前面的代碼。各位讀者一定要有耐心,暫時不理解沒有關系,根據教程中的代碼“照貓畫虎”即可,時間久了自然會接受。

    為什么使用?sockaddr_in 而不使用?sockaddr

    bind() 第二個參數的類型為 sockaddr,而代碼中卻使用 sockaddr_in,然后再強制轉換為 sockaddr,這是為什么呢?

    sockaddr 結構體的定義如下:

    ?
  • struct sockaddr{
  • sa_family_t sin_family; //地址族(Address Family),也就是地址類型
  • char sa_data[14]; //IP地址和端口號
  • };
  • 下圖是 sockaddr 與 sockaddr_in 的對比(括號中的數字表示所占用的字節數):


    sockaddr 和 sockaddr_in 的長度相同,都是16字節,只是將IP地址和端口號合并到一起,用一個成員 sa_data 表示。要想給 sa_data 賦值,必須同時指明IP地址和端口號,例如”127.0.0.1:80“,遺憾的是,沒有相關函數將這個字符串轉換成需要的形式,也就很難給 sockaddr 類型的變量賦值,所以使用 sockaddr_in 來代替。這兩個結構體的長度相同,強制轉換類型時不會丟失字節,也沒有多余的字節。

    可以認為,sockaddr 是一種通用的結構體,可以用來保存多種類型的IP地址和端口號,而 sockaddr_in 是專門用來保存 IPv4 地址的結構體。另外還有 sockaddr_in6,用來保存 IPv6 地址,它的定義如下:

    ?
  • struct sockaddr_in6 {
  • sa_family_t sin6_family; //(2)地址類型,取值為AF_INET6
  • in_port_t sin6_port; //(2)16位端口號
  • uint32_t sin6_flowinfo; //(4)IPv6流信息
  • struct in6_addr sin6_addr; //(4)具體的IPv6地址
  • uint32_t sin6_scope_id; //(4)接口范圍ID
  • };
  • 正是由于通用結構體 sockaddr 使用不便,才針對不同的地址類型定義了不同的結構體。

    connect() 函數

    connect() 函數用來建立連接,它的原型為:

    int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); //Linux int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen); //Windows

    各個參數的說明和 bind() 相同,不再贅述。

    總結

    以上是生活随笔為你收集整理的bind()和connect()函数:绑定套接字并建立连接的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。