gitee仓库:https://gitee.com/WangZihao64/linux/tree/master/chat_udp
预备知识
源IP地址和目的IP地址
它是用来标识网络中不同主机的地址。两台主机进行通信时,发送方需要知道自己往哪一台主机发送,这就需要知道接受方主机的的IP地址,也就是目的IP地址,因为两台主机是要进行通信的,所以接收方需要给发送方进行一个响应,这时接收方主机就需要知道发送方主机的IP地址,也就是源IP地址。有了这两个地址,两台主机才能够找到对端主机。
- 源IP地址: 发送方主机的IP地址,保证响应主机“往哪放”
- 目的IP地址: 接收方主机的IP地址,保证发送方主机“往哪发”
端口号
端口号是属于传输层协议的一个概念,它是一个16位的整数
,用来标识主机上的某一个进程
公网IP地址是用来标识全网内唯一的一台主机,端口号又是用来标识一台主机上的唯一一个进程,所以IP地址+端口号 就可以标识全网内唯一一个进程
TCP协议和UDP协议
传输层的协议
TCP(Transmission Control Protocol)协议: 传输控制协议
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
UDP(User Datagram Protocol)协议: 用户数据报协议
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
网络字节序
内存中的多字节数据的存储相对于内存地址有大端和小端之分:
- 大端字节序: 高位存放在低地址,低位存放在高地址
- 小端字节序: 低位存放在低地址,高位存放在高地址
如果双方主机的数据在内存存储的字节序不同,就会造成接收方收到的数据出现偏差,所以为了解决这个问题,有了下面的规定:
- TCP/IP协议规定,网络数据流采用
大端字节序
,不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据
下面几个API提供给我们用来做网络字节序和主机字节序的转换
#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);
说明:
- h代表的是host,n代表的是network,s代表的是16位的短整型,l代表的是32位长整形
- 如果主机是小端字节序,函数会对参数进行处理,进行大小端转换
- 如果主机是大端字节序,函数不会对这些参数处理,直接返回
socket API
API
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
sockaddr结构体
sockaddr_in的结构: 因为我们主要用到网络通信,所以这里主要介绍这个结构体,打开/usr/include/linux/in.h
并且搜索之下找到了struct的in_addr的说明,是32位的IP的地址。
地址转换函数
IP地址可以用点分十进制的字符串(如127.0.0.1),也可以用一个32位整数表示,其中就涉及到二者之间的转换,以下是二者相互转换的库函数:
字符串转in_addr的函数:
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
int inet_pton(int af, const char *src, void *dst);