1. 端口號
在同一台主機或設備上,可能有多個進程同時在使用TCP或UDP協議,端口號的作用就是區分這些不同的進程,即每個進程使用各自不同的端口號。
對於TCP協議和UDP協議,端口號都是用unsigned short類型表示,即端口號的范圍為0-65535,這65536個端口號被分為3段:
- 眾所周知端口(well-known port):范圍為0-1023,這些端口被賦予了專用功能,如FTP的21端口、Web的80端口等,應用程序不能將它們用作其他功能
- 已登記端口(registered port):范圍為1024-49151
- 臨時端口:范圍為49152-65535
well-known端口在Unix系統中稱為保留端口,使用保留端口的服務器程序,必須以root用戶啟動運行。
2. 套接字對
TCP套接字對是定義TCP連接兩個端點的四元組,包括:本地IP地址、本地端口號、對端IP地址、對端端口號,每個端點的IP地址和端口號通常稱為一個套接字。
TCP套接字對可用於唯一標識一個網絡中的TCP連接。
3. 套接字地址結構
大多數套接字API都需要一個指向套接字地址結構的指針作為函數參數,每個協議族都定義它自己的套接字地址結構,這些結構的名字均以sockaddr_開頭,並以對應每個協議族的唯一后綴結尾。
IPv4套接字地址結構
IPv4套接字地址結構通常也稱為“網際套接字地址結構”,它以sockaddr_in
命名,定義在<netinet/in.h>
頭文件中。
#include <netinet/in.h>
struct in_addr
{
in_addr_t s_addr; //32-bit IPv4 address, network byte ordered
};
struct sockaddr_in
{
uint8_t sin_len; //length of structure
sa_family_t sin_family; //AF_INET
in_port_t sin_port; //16-bit TCP Or UDP port number, network byte ordered
struct in_addr sin_addr; //IPv4 address structure
char sin_zero[8]; //unused
};
- IPv4地址和端口號在套接字地址結構中必須以網絡字節序存儲
- IPv4地址可以按sockaddr_in.sin_addr和sockaddr_in.sin_addr.s_addr兩種方式引用
- 按照使用慣例,總是先把整個結構請0,再填寫相應字段
- 網絡通訊時,傳遞的是套接字地址結構中的某些字段,結構本身僅在當前主機上使用
通用套接字地址結構
通用套接字地址結構struct sockaddr
定義在<sys/socket.h>
頭文件中。
#include <sys/socket.h>
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[4];
};
對於應用程序開發人員來說,通用套接字地址結構的唯一用途就是:將特定協議的套接字地址結構指針強制類型轉換為通用套接字地址結構指針,並傳遞給套接字API。
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
4. 字節排序函數
- IPv4地址和端口號必須以網絡字節序存儲,TCP/IP協議中的網絡字節序采用大端字節序
- 某個給定系統使用的字節序叫做主機字節序,主機字節序可能為大端字節序或小端字節序
- 在填寫套接字地址結構時,必須將IPv4地址和端口號從主機字節序轉換為網絡字節序
- 字節排序函數用於在主機字節序和網絡字節序之間進行轉換
#include <arpa/inet.h>
/* 主機字節序——>網絡字節序 */
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
/* 網絡字節序——>主機字節序 */
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
上面4個字節排序函數只適用於整數類型,因此在套接字編程中:
- 字節排序函數常用來轉換16位端口號和IPv4通配地址INADDR_ANY
- 其余IP地址則一般使用地址轉換函數進行轉換
5. 地址轉換函數
地址轉換函數用於在IP地址的字符串格式和網絡字節序格式之間進行轉換。
- inet_addr()、inet_aton()、inet_ntoa()僅適用於IPv4地址轉換
- inet_pton()和inet_ntop()適用於IPv4和IPv6地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* 字符串——>網絡字節序;
* 成功返回網絡字節序IPv4地址,失敗返回INADDR_NONE;
*/
in_addr_t inet_addr(const char *str);
/*
* 字符串——>網絡字節序,並通過addr指針保存;
* 若字符串有效則成功返回1,否則失敗返回0.
*/
int inet_aton(const char *str, struct in_addr *addr);
/*
* 網絡字節序——>字符串;
* 返回字符串格式的IPv4地址.
*/
char *inet_ntoa(struct in_addr addr);
上面三個函數有兩點需要注意:
- INADDR_NONE是一個32位都為1的整數,因此inet_addr()不能轉換地址255.255.255.255
- inet_ntoa()的參數是結構體變量,而非結構體指針