IP 地址和端口号
IP 地址可以确定网络中的一台主机,端口号可以确定一台主机中的进程。端口号和进程 PID 不同的是,并不是所有进程都要对外提供网络请求,端口号是网络级别的概念,PID 是系统概念。
网络字节序
发送主机通常将发送缓冲区的数据按照从低到高的顺序发出,而接收主机把从网络上接收到的字节依次保存在接收缓冲区中,也是按照从低到高的顺序保存。但是不同主机的字节序可能不同,这就造成数据读取错误。为了解决这个问题,网络协议对网络数据流的字节序做了统一规定。但是在发送 IP 地址和端口号时,仍需要手动转换。
套接字 API
创建套接字 int socket(int domain, int type, int protocol)
所需头文件:
- <sys/types.h>
- <sys/socket.h>
参数说明: - domain 协议,如 IPV4,IPV6,本地通信
- type 传输层提供服务类型:TCP、UDP
- protocol 如果为 0,将自动推导
本质上,创建套接字和创建文件相同,因此还需要将文件和网络进行关联(绑定)。
绑定套接字 int bind(int fd, const sockaddr* addr, socklen_t len)
所需头文件:
- <sys/types.h>
- <sys/socket.h>
参数说明: - fd 文件描述符
- addr 网络相关信息数据,包含 16 位端口号,32 位 IP 地址
- len a网络相关信息的长度
绑定套接字本质上就是将网络相关信息填充到套接字,将文件与网络 IP 和端口号相关联。
服务器程序绑定端口的原因:服务器程序是为了提供服务,需要其他人知道服务的 IP 和端口,并且 bind 之后,不能轻易改变。
发送数据 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)
所需头文件:
- <sys/socket.h>
- <sys/types.h>
函数功能为往接收端发送大小为 len 的消息 buf,函数返回实际发送的数据大小。发送时,需要设置接收方的 IP 和端口。
接收数据 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
所需头文件:
- <sys/types.h>
- <sys/socket.h>
函数功能为接收大小为 len 的消息并存储到 buf,函数返回实际接收到数据的大小。接收时,无需设置发送方的 IP 和端口。
实现 UDP 通信
// Server
lass UdpServer
{
private:
std::string ip;
int port;
int sockfd;
public:
UdpServer(std::string _ip, int _port = DEFAULT):ip(_ip),port(_port),sockfd(-1)
{
}
bool InitUdpServer()
{
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
std::cerr << "socket error" << std::endl;
return false;
}
std::cout << "socket create