3.1 分配給套接字的IP地址與端口號
IP是InternetProtocol(網絡協議)的簡寫,是爲收發網絡數據而分配給計算機的值。
端口號並非賦予計算機的值,而是爲區分程序中創建的套接字而分配給套接字的序號。
1. 網絡地址(Internet Address)
- IPv4 4字節地址族
- IPv6 16字節地址族
IPv4標準的4字節IP地址分爲網絡地址和主機(指計算機)地址,且分爲A、B、C、D、E等類型。
關注:路由器與交換機的作用。
2. 用於區分套接字的端口號
計算機中一般配有NIC(NetworkInterface Card,網絡接口卡)數據傳輸設備。
通過NIC向計算機內部傳輸數據時會用到IP。操作系統負責把傳遞到內部的數據適當分配給套接字,這時就要利用端口號。
端口號就是在同一操作系統內爲區分不同套接字而設置的,因此無法將1個端口號分配給不同套接字。
另外,端口號由16位構成,可分配的端口號範圍是0~65535。但0~1023是知名端口(Well-Known PORT),一般分配給特定應用程序。
另外,雖然端口號不能重複,但TCP套接字和UDP套接字不會共用端口號,所以允許跨協議的端口號重複。
總之,數據傳輸目標地址同時包含IP地址和端口號,只有這樣,數據纔會被傳輸到最終的目的應用程序(應用程序套接字)。
3.2 地址信息的表示
應用程序中使用的IP地址和端口號以結構體的形式給出了定義。
1. 表示IPv4地址的結構體
struct sockaddr_in
{
sa_family_t sin_family; // 地址族
uint16_t sin_port; //16位TCP/IP端口號,網絡字節序
struct in_addr sin_addr; // 32位IP地址,網絡字節序
char sin_zero[8]; // 不使用,僅爲使sockaddr_in與sockaddr結構體保持一致
};
struct in_addr
{
In_addr_t s_addr; // 32位IPv4地址
};
如果使用int32_t類型的數據,就能保證在任何時候都佔用4字節,即使將來用64位表示int類型也是如此。
sockaddr_in 結構體變量地址值將以如下方式傳遞給bind函數:
struct sockaddr
{
sa_family_t sin_family; // 地址族
char sa_data[14]; // 地址信息
};
此結構體成員sa_data保存的地址信息中需包含IP地址號和端口號,剩餘部分應填充0,這也是bind函數的要求。
sockaddr_in是保存IPv4地址信息的結構體,而sockaddr並非只爲IPv4設計。所以仍需單獨指定sin_family。
3.3 網絡字節序與地址轉換
代表CPU數據保存方式的主機字節序(HostByte Order)在不同CPU中也各不相同。在通過網絡傳輸數據時,約定:統一使用網絡字節序(NetworkByte Order),即大端字節序。
unsignedshort htons(unsigned short);
unsignedshort ntohs(unsigned short);
unsignedlong htonl(unsigned long);
unsignedlong ntohl(unsigned long);
htons中的h代表主機(host)字節序;n代表網絡(network)字節序。
s指的是short,l指的是long。(Linux中long類型佔用4個字節)
通常,以s作爲後綴函數用於端口號轉換,以l作爲後綴函數用於IP地址轉換。
問:數據傳輸過程中都需要轉換嗎?
答:除了向sockaddr_in結構體變量填充數據外,其他情況無需考慮字節序問題。
3.4 網絡地址的初始化與分配
1. 將字符串信息轉換爲網絡字節序的整數型
sockaddr_in中保存IP地址信息的成員爲32位整數型,因此,爲了分配IP地址,需要將點分十進制(DottedDecimal Notation)的IP地址化爲32位整數型數據。
#include<arpa/inet.h>
in_addr_t inet_addr(const char *string);
//成功時返回32位大端序整數型值,失敗時返回INADDR_NONE
該函數將字符串形式的IP地址轉換成32位大端序(網絡序)整數型IP地址。還可檢測無效IP地址。
#include<arpa/inet.h>
int inet_aton(const char * string, struct in_addr *addr);
該函數與inet_addr函數功能相同,但其利用了in_addr結構體,使用頻率很高;與inet_aton作用相反的函數:
#include<arpa/inet.h>
char* inet_ntoa(struct in_addr addr);
注意:調用時應將字符串信息複製到其他內存空間。(通過strcpy函數)
2. 常用的網絡地址初始化方法
atoi函數把字符串轉換成整型。
3. INADDR_ANY
每次創建服務器端套接字都要輸入IP地址會有些繁瑣,此時可如下初始化地址信息:
struct sockaddr_inaddr;
......
addr.sin_addr.s_addr= htonl(INADDR_ANY);
若採用這種方式,則可自動獲取運行服務器端的計算機IP地址,不必親自輸入。而且,若同一計算機中已分配多個IP地址(多宿主(Multi-homed)計算機,一般路由器屬於這一類),則只要端口號一致,就可以從不同IP地址接收數據。(實際IP地址的個數與計算機中安裝的NIC的數量相等,若只有一個NIC,則直接使用INADDR_ANY)
127.0.0.1是回送地址(loopback address),指的是計算機自身IP地址。若用實際IP地址代替此地址也能正常運轉。
4. 向套接字分配網絡地址
#include<sys/socket.h>
int bind(int sockfd, structsockaddr *myaddr, socklen_t addrlen);
-sockfd:要分配地址信息(IP地址和端口號)的套接字文件描述符;
-myaddr: 存有地址信息的結構體變量地址值;
-addrlen: 第二個結構體變量的長度;