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