C语言网络编程:bind函数详解
文章目錄
- 函數功能
- 函數頭文件
- 函數使用
- 函數參數
- 函數舉例
- 為什么需要bind函數
- 服務器如何知道客戶端的ip和端口號
- htons函數
- `htons`兄弟函數`htonl`,`ntohs`,`ntohl`
- 為什么要進行端口的大小端序的轉換
- `inet_addr`函數
函數功能
bind API能夠將套接字文件描述符、端口號和ip綁定到一起
注意:
綁定的一定是自己的 ip和和端口,不是對方的;比如對于TCP服務器來說綁定的就是服務器自己的ip和端口
函數頭文件
#include <sys/types.h> /* See NOTES */#include <sys/socket.h>
函數使用
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
函數參數
sockfd表示socket函數創建的通信文件描述符addrlen表示所指定的結構體變量的大小addr表示struct sockaddr的地址,用于設定要綁定的ip和端口struct sockaddr {sa_family_t sa_family;char sa_data[14]; }sa_family用于指定AF_***表示使用什么協議族的ip
sa_data存放ip和端口
這里有一個問題,直接向sa_data中寫入ip和端口號有點麻煩,內核提供struct sockaddr_in結構體進行寫入,通過/usr/include/linux/in.h可以看到結構體原型
使用該結構體時需要包含<netinet/in.h>頭文件,且sockaddr_in結構體是專門為tcp/ip協議族使用,其他協議族需要使用其對應的轉換結構體,比如“域通信協議族” 使用的是sockaddr_un結構體
可以看到以上struct sockaddr_in {__kernel_sa_family_t sin_family; /* Address family */__be16 sin_port; /* Port number */struct in_addr sin_addr; /* Internet address *//* Pad to size of `struct sockaddr'. 設置IP端口號這個成員暫時用不到 */unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -sizeof(unsigned short int) - sizeof(struct in_addr)]; };/* Internet address.填補相比于struct sockaddr所缺的字節數,保障強制轉換不要出錯 */ struct in_addr {__be32 s_addr; // __be32是32位的unsigned int ,因為ipv4是無符號32位整型 };sockaddr_in結構體中存放的端口和ip是分開的,所以設置起來非常方便,使用struct sockaddr_in設置后,讓后將其強制轉換為struct sockaddr類型,然后傳遞給bind函數即可
函數舉例
struct sockaddr_in addr;
addr.sin_family = AF_INET; //設置tcp協議族
addr.sin_port = htons(6789); //設置端口號
addr.sin_addr.s_addr = inet_addr("192.168.1.105"); //設置ip地址ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));
如果是跨局域網或者城域網通信,這里設置的ip地址一定為通信設備所在路由器的外網ip地址。
如下c代碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h> //struct sockadd_in 結構體的頭文件
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>void print_err(char *str, int line, int err_no) {printf("%d, %s :%s\n",line,str,strerror(err_no));_exit(-1);
}int main()
{int skfd = -1;skfd = socket(AF_INET, SOCK_STREAM, 0);if ( -1 == skfd) {print_err("socket failed",__LINE__,errno);}struct sockaddr_in addr;addr.sin_family = AF_INET; //設置tcp協議族addr.sin_port = htons(6789); //設置端口號addr.sin_addr.s_addr = inet_addr("192.168.102.169"); //設置ip地址int ret = bind(skfd, (struct sockaddr*)&addr, sizeof(addr));if ( -1 == ret) {print_err("bind failed",__LINE__,errno);}return 0;
}
為什么需要bind函數
bind函數就是讓套接字文件在通信時使用固定的IP和端口號(針對服務器來說)
可以看到如上實現代碼,調用socket函數創建的套接字僅僅執行了通信等協議,但是并沒有指定通信時所需的ip地址和端口號
- ip 是對方設備的唯一標識
- 端口號 區分同一臺計算機上的不同的網絡通信進程
如果不調用bind函數指定ip和端口,則會自己指定一個ip和端口,此時違背了TCP通信的可靠性和面向連接的特點。
服務器如何知道客戶端的ip和端口號
可以通過上文TCP通信模型中看到,客戶端通信時不需要指定ip和端口號,直接創建一個socket套接字文件描述符即可參與通信。
此時當客戶端和服務器建立連接的時候,服務器會從客戶的數據包中提取出客戶端ip和端口,并保存起來,如果是跨網通信,那么記錄的就是客戶端所在路由器的公網ip
htons函數
#include <arpa/inet.h>uint16_t htons(uint16_t hostshort);函數全拼為host to net short- 函數功能
a. 將端口從"主機端序" 轉為 “網絡端序”
b. 如果給定的端口不是short,則轉為short - 返回值: 函數的調用永遠都是成功的,返回轉換后的端口號
htons兄弟函數htonl,ntohs,ntohl
htonl與htons唯一的區別時,轉換完的端口號為longntohs與htons恰好相反,是從網絡字節序轉換為主機字節序ntohl表示從網絡字節轉換為主機序,同時轉換完的端口號為long
為什么要進行端口的大小端序的轉換
大端序:
大端模式,是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中,這樣的存儲模式有點兒類似于把數據當作字符串順序處理:地址由小向大增加,而數據從高位往低位放;
小端序:
小端模式,是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中,這種存儲模式將地址的高低和數據位權有效地結合起來,高地址部分權值高,低地址部分權值低,和我們的邏輯方法一致。
大小端序是由具體的操作系統來決定的
可以使用如下代碼測試系統是大端還是小端:
#include <stdio.h>
int main()
{int a = 1;char pc = *(char*)(&a);if (pc == 1)printf("第一個字節為1,小端存儲\n");elseprintf("第一個字節為0,大端存儲\n");return 0;
}
同樣,網絡通信的時候發送端計算機和接收端計算機可能端序不一致,比如發送者是大端序,接受者是小端序,如果通信時數據的端序處理不好很可能出現亂碼,甚至無法接收到數據。
如果發送者和接受者端序一致則也能夠正常傳輸數據,不用htons函數進行轉換,不過保證數據序列正確的得進行傳輸,建議使用htons函數進行端口號的轉換
inet_addr函數
<sys/socket.h> <netinet/in.h> <arpa/inet.h>in_addr_t inet_addr(const char *cp);- 函數功能:
a. 將字符串形式的IP "192.168.102.169"轉換為IPV4的32位無符號整型數的IP
b. 將無符號整型數的ip,從主機端序轉為網絡端序 - 參數:字符串形式的ip
- 返回值:永遠成功,返回網絡端序的、32位無符號整型數的ip
總結
以上是生活随笔為你收集整理的C语言网络编程:bind函数详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 硅藻泥多少钱一平方啊?
- 下一篇: C语言网络编程:socket函数