Socket网络编程

结构体

各类结构体以不同方式存放不同类型、不同数量的Socket信息,作为不同Socket接口函数的输入参数使用

in_addr

表示32位的IPv4地址

1
2
3
4
5
6
7
8
9
#include <arpa/inet.h>

// 用于表示32位的IPv4地址
struct in_addr{
in_addr_t s_addr;
}

// int_addr_t类型为unsigned int,长度为4字节(32位),字节序为网络顺序(大端字节序)
// 如IP地址192.168.3.144,用int_addr_t类型存储为0Xc0a80390

sockaddr

套接字结构

1
2
3
4
5
6
7
8
9
#include <sys/socket.h>

struct sockaddr{
sa_family_t sin_family; // 协议族
char sa_data[14]; // 套接字中的目标IP和端口
}

// sa_family_t类型为unsigned short,长度为2字节(16位)
// 取值包括:AF_INET、AF_INET6、AF_UNSPE

sockaddr_in

套接字结构

1
2
3
4
5
6
7
8
9
10
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

struct sockaddr_in{
sa_family_t sin_family; // 协议族
unsigned short sin_port; // 端口号
struct in_addr sin_addr; // IP地址结构体
char sin_zero[8]; // 填充,为了与sockaddr结构体在内存中对齐,方便相互转换
}

函数

字节序转换

字节序指在内存中存放大于一个字节的数据时,数据各字节的存放顺序。
字节序可分为:
大端字节序:高位字节在前,低位字节在后
小端字节序:低位字节在前,高位字节在后

设计两种字节序的原因:
大端字节序符合人类的读写习惯,比如我们想存放“一千二百三十四”这个十进制数字(转换为十六进制为0x04D2),用大端字节序存储后,当我们读取时还是0x04D2。
但如果用小端字节序存储,读取时就变为0xD204。虽然小端字节序不符合人类读写习惯,但有利于提高计算效率,因为计算机计算时都是从数据的低位开始计算。

主机字节序:小端字节序
网络字节序:大端字节序

普通数据的字节序转换

主机字节序—>网络字节序

功能:无符号32位/16位整型数据转换
参数:主机字节序数据
返回值:网络字节序数据

1
2
3
4
#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);

网络字节序—>主机字节序

功能:无符号32位/16位整型数据转换
参数:网络字节序数据
返回值:主机字节序数据

1
2
3
4
#include <arpa/inet.h>

uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

IP地址的字节序转换

字符串—>网络字节序

inet_pton

参数af:地址协议族(AF_INET、AF_INET6)
参数strptr:指向IP字符串存储地址的指针
参数addrptr:指向网络字节序IP数据存储地址的指针
返回值:成功返回1,输入的IP地址无效返回0,出错返回-1

1
2
3
#include <arpa/inet.h>

int inet_pton(int af, const char *strptr, void *addrptr);
inet_aton

参数cp:指向IP字符串存储地址的指针
参数inp:指向in_addr结构体的指针,结构体内s_addr存储转换后的格式为网络字节序的32位二进制格式的IP
返回值:成功返回非0,IP地址无效返回0

1
2
3
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);
inet_addr

参数cp:指向IP字符串存储地址的指针
返回值:in_addr_t类型变量,存储网络字节序的32位二进制格式的IP

1
2
3
#include <arpa/inet.h>

in_addr_t inet_addr(const char *cp);

字符串—>主机字节序

inet_network

参数cp:指向IP字符串存储地址的指针
返回值:in_addr_t类型变量,存储主机字节序的32位二进制格式的IP

1
2
3
#include <arpa/inet.h>

in_addr_t inet_network(const char *cp);

网络字节序—>字符串

inet_ntop

参数af:地址协议族
参数addrptr:指向网络字节序IP数据存储地址的指针
参数strptr:指向IP字符串存储地址的指针
参数len:IP字符串存储地址空间大小
返回值:成功返回参数strptr的指针,失败返回NULL且修改errno的值。

1
2
3
#include <arpa/inet.h>

const char *inet_ntop(int af, const void *addrptr, char *strptr, size_t len);
inet_ntoa

参数in:in_addr结构体,结构体内s_addr存储网络字节序的32位二进制格式的IP
返回值:指向IP字符串存储地址的指针

1
2
3
#include <arpa/inet.h>

char *inet_ntoa(struct in_addr in);

测试

测试inet_pton、inet_ntop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>   
#include <arpa/inet.h>
#include <string.h>

int main(){
// 测试inet_pton
char ip_str[] ="193.168.1.10";
in_addr_t ip_addr;
int re = inet_pton(AF_INET, ip_str, (void *)&ip_addr);
printf("inet_pton返回值 = %d\n",re);
// inet_pton返回值 = 1
printf("ip_addr = %d\n",ip_addr);
// ip_addr = 167880897

printf("-------\n");
// 测试inet_ntop
char ip_str2[INET_ADDRSTRLEN];
const char *re2 = inet_ntop(AF_INET, (const void *)&ip_addr, ip_str2, sizeof(ip_str2));
printf("inet_ntop返回值 = %s\n", re2);
// inet_ntop返回值 = 193.168.1.10
printf("ip_str2 = %s\n",ip_str2);
// ip_str2 = 193.168.1.10
}

测试inet_aton、inet_ntoa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>   
#include <arpa/inet.h>
#include <string.h>

int main(){
// 测试inet_aton
char ip_str[] ="193.168.1.10";
struct in_addr ip_addr;
int re = inet_aton(ip_str, &ip_addr);
printf("inet_aton返回值 = %d\n",re);
// inet_aton返回值 = 1
printf("ip_addr.s_addr = %d\n",ip_addr.s_addr);
// ip_addr.s_addr = 167880897
printf("-------\n");

// 测试inet_ntoa
char *ip_str2;
ip_str2 = inet_ntoa(ip_addr);
printf("inet_ntoa返回值 = %s\n", ip_str2);
// inet_ntoa返回值 = 193.168.1.10
}

测试inet_addr、inet_network

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>   
#include <arpa/inet.h>
#include <string.h>

int main(){
// 测试inet_addr
char ip_str[] ="193.168.1.10";
struct in_addr ip_addr1;
ip_addr1.s_addr = inet_addr(ip_str);
printf("inet_addr返回值 = %u\n",ip_addr1.s_addr);
// inet_addr返回值 = 167880897
printf("-------\n");

// 测试inet_network
struct in_addr ip_addr2;
ip_addr2.s_addr = inet_network(ip_str);
printf("inet_network返回值 = %u\n", ip_addr2.s_addr);
// inet_network返回值 = 3249013002
}

IP地址处理

IP地址—>主机地址

参数in:in_addr结构体
返回值:in_addr_t类型变量,存储主机字节序的主机地址数据

1
2
3
4
5
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

in_addr_t inet_lnaof(struct in_addr in);

IP地址—>网络地址

参数in:in_addr结构体
返回值:in_addr_t类型变量,存储主机字节序的网络地址数据

1
2
3
4
5
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

in_addr_t inet_netof(struct in_addr in);

主机地址+网络地址—>IP地址

参数net:in_addr_t类型变量,存储网络字节序的网络地址数据
参数host:in_addr_t类型变量,存储网络字节序的主机地址数据
返回值:in_addr结构体,内部包含in_addr_t类型变量,存储网络主机字节序的IP地址数据

1
2
3
4
5
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host);

测试

测试inet_lnaof、inet_netof、inet_makeaddr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>   
#include <arpa/inet.h>
#include <string.h>

int main(){
char ip_str1[] = "44.135.86.12";
char ip_str2[] = "172.16.23.95";
char *ip_str;

char *net_str1;
char *net_str2;

char *host_str1;
char *host_str2;

struct in_addr ip_addr1;
struct in_addr ip_addr2;
struct in_addr ip_addr;

struct in_addr net_addr1;
struct in_addr net_addr2;

struct in_addr host_addr1;
struct in_addr host_addr2;

inet_aton(ip_str1,&ip_addr1); // 将IP地址从字符串格式转为网络字节序的32位二进制格式

host_addr1.s_addr = inet_lnaof(ip_addr1); // 将IP地址转换为主机地址,格式为主机字节序的32位二进制格式
host_addr1.s_addr = htonl(host_addr1.s_addr); // 主机字节序转换为网络字节序
host_str1 = inet_ntoa(host_addr1); // 将主机地址从网络字节序的32位二进制格式转换为字符串格式
printf("主机地址:%s\n",host_str1);

net_addr1.s_addr = inet_netof(ip_addr1); // 将IP地址转换为网络地址,格式为主机字节序的32位二进制格式
net_addr1.s_addr = htonl(net_addr1.s_addr); // 主机字节序转换为网络字节序
net_str1 = inet_ntoa(net_addr1); // 将主机地址从网络字节序的32位二进制格式转换为字符串格式
printf("网络地址:%s\n",net_str1);

host_addr1.s_addr = ntohl(host_addr1.s_addr); // 网络字节序转换为主机字节序
net_addr1.s_addr = ntohl(net_addr1.s_addr); // 网络字节序转换为主机字节序
ip_addr = inet_makeaddr(net_addr1.s_addr, host_addr1.s_addr); // 网络地址和主机地址合成IP地址,格式为网络字节序的32位二进制格式
ip_str = inet_ntoa(ip_addr); // 将IP地址从网络字节序的32位二进制格式转换为字符串格式
printf("IP地址:%s\n",ip_str);

inet_aton(ip_str2,&ip_addr2);

host_addr2.s_addr = inet_lnaof(ip_addr2);
host_addr2.s_addr = htonl(host_addr2.s_addr);
host_str2 = inet_ntoa(host_addr2);
printf("主机地址:%s\n",host_str2);

net_addr2.s_addr = inet_netof(ip_addr2);
net_addr2.s_addr = htonl(net_addr2.s_addr);
net_str2 = inet_ntoa(net_addr2);
printf("网络地址:%s\n",net_str2);
}

常规SocketC

TCP

服务端

客户端

UDP

服务端

客户端