客户端的ip和端口的发送,存储位置和服务端的ip和端口的绑定
客户端 IP 和端口
1. 客户端 IP 和端口的确定
- 客户端 IP:客户端的 IP 地址由其所在网络环境决定。如果客户端处于局域网中,可能通过 DHCP 协议从局域网内的 DHCP 服务器获取一个内部私有 IP 地址;如果是直接连接到公网,可能会有一个公网 IP 地址。
- 客户端端口:客户端端口一般由操作系统动态分配。当客户端调用
connect
函数发起连接时,操作系统会从可用的端口范围(通常是 1024 - 65535)中选择一个未被使用的端口号分配给该套接字。
2. 客户端 IP 和端口的发送
当客户端调用 connect
函数时,操作系统会将客户端的 IP 地址和动态分配的端口号封装在 TCP 连接请求(SYN 报文)中。connect
函数原型如下:
收起
c
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd
:客户端的套接字描述符。addr
:指向服务器地址信息(IP 地址和端口号)的结构体指针。addrlen
:addr
所指向结构体的长度。
客户端通过网络协议栈将封装好的 SYN 报文发送给服务器,这个报文中就包含了客户端的 IP 地址和端口号。
3. 客户端 IP 和端口的存储位置
客户端本身不会专门存储用于发送给服务器的 IP 地址和端口号信息。在客户端代码里,主要关注的是要连接的服务器的地址信息。不过,在网络协议栈层面,会记录客户端套接字对应的 IP 地址和端口号,以便进行数据的收发和连接管理。
服务端 IP 和端口
1. 服务端 IP 和端口的绑定
在服务端,需要将套接字与特定的 IP 地址和端口号进行绑定,这样客户端才能通过该 IP 地址和端口号找到并连接到服务器。服务端进行绑定操作主要通过以下几个步骤:
- 创建套接字:使用
socket
函数创建一个套接字,示例如下:
收起
c
#include <sys/socket.h>
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("socket creation failed");
return -1;
}
这里 AF_INET
表示使用 IPv4 地址族,SOCK_STREAM
表示使用面向连接的 TCP 协议。
- 准备地址信息结构体:定义一个
struct sockaddr_in
类型的结构体,并填充服务器的 IP 地址和端口号信息。示例如下:
收起
c
#include <arpa/inet.h>
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有可用的网络接口
server_addr.sin_port = htons(8888); // 端口号,使用 htons 进行字节序转换
INADDR_ANY
表示服务器将监听所有可用的网络接口,这样无论客户端通过哪个 IP 地址访问服务器,服务器都能接受连接。htons
函数用于将主机字节序转换为网络字节序,确保在不同字节序的系统之间能正确通信。
- 绑定操作:使用
bind
函数将套接字与地址信息进行绑定,示例如下:
收起
c
if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
close(sockfd);
return -1;
}
2. 服务端接收客户端 IP 和端口信息
服务端调用 accept
函数接受客户端连接时,会获取客户端的 IP 地址和端口号信息。accept
函数原型如下:
收起
c
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd
:服务端的监听套接字描述符。addr
:指向用于存储客户端地址信息的结构体指针。addrlen
:指向socklen_t
类型的指针,传入时表示addr
所指向结构体的长度,返回时表示实际存储的客户端地址信息的长度。
当 accept
函数成功接受一个客户端连接时,操作系统会从接收到的 TCP 连接请求报文中提取客户端的 IP 地址和端口号信息,并将其填充到 addr
所指向的结构体中。示例如下:
收起
c
struct sockaddr_in client_addr;
socklen_t client_addrlen = sizeof(client_addr);
int client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addrlen);
if (client_sockfd == -1) {
perror("accept failed");
close(sockfd);
return -1;
}
// 打印客户端 IP 地址和端口号
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(client_addr.sin_addr), client_ip, INET_ADDRSTRLEN);
printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));
inet_ntop
函数用于将二进制的 IP 地址转换为点分十进制的字符串形式,ntohs
函数用于将网络字节序转换为主机字节序。
综上所述,客户端的 IP 和端口由操作系统在 connect
时封装在 SYN 报文中发送给服务器,服务端通过 bind
操作将套接字与特定的 IP 地址和端口号绑定,通过 accept
函数获取客户端的 IP 地址和端口号信息。