国家城乡和住房建设部网站首页,外贸专业网站制作,目前网站开发技术,做网站要多少钱汉狮文章目录前言一#xff0c;socket二#xff0c;服务端socket3-1 创建socket3-2 绑定地址和端口3-3 接收数据3-4 回复数据3-5关闭socket3-6 完整代码三#xff0c;客户端socket3-1 为什么客户端通常不需要手动定义 IP 和端口前言
学习 socket 编程的意义在于#xff1a;它让…
文章目录前言一socket二服务端socket3-1 创建socket3-2 绑定地址和端口3-3 接收数据3-4 回复数据3-5关闭socket3-6 完整代码三客户端socket3-1 为什么客户端通常不需要手动定义 IP 和端口前言
学习 socket 编程的意义在于它让你掌握计算机之间通信的核心原理能亲手实现聊天程序、文件传输、简易服务器等网络应用同时这是理解 TCP/IP 协议、深入系统编程和进入后台开发、分布式系统的基础技能也是面试和工程实践中必不可少的知识。 一socket
socket套接字 是操作系统提供的一种 通信机制最常用于网络通信在编程时你可以把它当作 特殊的文件描述符FD既能读、也能写文件用来在本地磁盘读写数据而 socket 用来在不同主机之间交换数据。你可以把 socket 看作网络文件只是读写的数据不是在磁盘上而是发送到网络上的另一个进程。 二服务端socket
主要作用接收客户端请求并回复服务器端使用socket分为几个阶段创建 socket - 绑定地址和端口 - 等待客户端发送数据 / 监听连接 - 处理数据或回复 - 关闭 socket 3-1 创建socket
socket函数原型
int socket(int domain, int type, int protocol);domain用于选择网络协议协议类型一般有值AF_INET IPv4 网络协议AF_INET6 IPv6 网络协议AF_UNIX/AF_LOCAL 本地进程间通信type指定传输方式主要有两种SOCK_STREAM 面向连接基于字节流TCPSOCK_DGRAM 无连接基于报文UDPprotocol通常指定具体协议0 默认协议IPPROTO_TCP 明确选择 TCPIPPROTO_UDP 明确选择 UDP
在下面的代码示例中我们会选择
int sockfd socket(AF_INET, SOCK_DGRAM, 0);创建一个UDP的套接字 3-2 绑定地址和端口
struct sockaddr_in servaddr;
servaddr.sin_family AF_INET;
servaddr.sin_addr.s_addr INADDR_ANY; // 任意 IP
servaddr.sin_port htons(12345); // 端口
bind(sockfd, (struct sockaddr*)servaddr, sizeof(servaddr));上面的struct sockaddr_in servaddr创建一个 IPv4 地址结构体 sockaddr_in用来保存服务端的 IP 地址和端口信息结构体定义大致如下
struct sockaddr_in {short sin_family; // 地址族 (AF_INET)unsigned short sin_port; // 端口号struct in_addr sin_addr; // IP 地址char sin_zero[8]; // 填充为与 struct sockaddr 同大小
};解释上述代码
servaddr.sin_family AF_INET;设置 地址族为 IPv4 (AF_INET)告诉内核这是一个 IPv4 的 socket
servaddr.sin_addr.s_addr INADDR_ANY; // 任意 IPINADDR_ANY 0.0.0.0表示服务端 绑定本机所有可用 IP 地址如果服务器有多个网卡客户端发送到任意 IP 都能被接收
servaddr.sin_port htons(12345); // 端口设置端口号为 12345htons host to network short主机字节序 → 网络字节序网络通信使用 大端序保证不同平台可以正确解析端口
bind(sockfd, (struct sockaddr*)servaddr, sizeof(servaddr));bind() 将 socket 文件描述符 与 IP 端口 绑定
参数解释
sockfd之前 socket() 返回的文件描述符
(struct sockaddr*)servaddr 地址信息强制类型转换为通用 sockaddr
sizeof(servaddr) 结构体大小 3-3 接收数据
char buffer[100];
struct sockaddr_in cliaddr {};
socklen_t len sizeof(cliaddr);int n recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr * ) cliaddr, len);这段代码在一个 UDP 服务器程序中从客户端接收数据存储到 buffer 中同时记录客户端的地址信息cliaddr并返回接收的字节数n。
char buffer[100];定义一个字符数组 buffer大小为 100 字节用来存储从客户端接收的数据
struct sockaddr_in cliaddr {};定义一个 sockaddr_in 结构体变量 cliaddr用于存储客户端的地址信息如 IP 地址和端口号。{} 初始化所有字段为 0
socklen_t len sizeof(cliaddr);定义一个 socklen_t 类型的变量 len初始化为 cliaddr 的大小通常 16 字节struct sockaddr_in 的大小。 3-4 回复数据
const char* reply Hello Client;sendto(sockfd, reply, strlen(reply), 0, (struct sockaddr*)cliaddr, len); // 回复这段代码用与服务器回复客户端信息
其它的不再过多赘述
(struct sockaddr*)cliaddr客户端地址结构体告诉内核消息发送到哪里 3-5关闭socket
close(sockfd);不关闭会造成资源泻漏 3-6 完整代码
#include iostream
#include arpa/inet.h
#include unistd.hint main() {int sockfd socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP socketif (sockfd 0) { perror(socket); return -1; }sockaddr_in servaddr{};servaddr.sin_family AF_INET;servaddr.sin_addr.s_addr INADDR_ANY;servaddr.sin_port htons(12345);bind(sockfd, (sockaddr*)servaddr, sizeof(servaddr)); // 绑定端口char buffer[100];sockaddr_in cliaddr{};socklen_t len sizeof(cliaddr);int n recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)cliaddr, len); // 接收消息buffer[n] \0;std::cout Server received: buffer std::endl;const char* reply Hi Client;sendto(sockfd, reply, strlen(reply), 0, (sockaddr*)cliaddr, len); // 回复客户端close(sockfd);return 0;
} 三客户端socket
客户端socket代码我直接展示出来吧我们理解了服务端之后客户端是非常好理解的
#include iostream
#include arpa/inet.h
#include unistd.hint main() {int sockfd socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP socketif (sockfd 0) { perror(socket); return -1; }sockaddr_in servaddr{};servaddr.sin_family AF_INET;servaddr.sin_port htons(12345);servaddr.sin_addr.s_addr inet_addr(127.0.0.1); // 本机测试const char* msg Hello Server;sendto(sockfd, msg, strlen(msg), 0, (sockaddr*)servaddr, sizeof(servaddr)); // 发送消息char buffer[100];socklen_t len sizeof(servaddr);int n recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (sockaddr*)servaddr, len); // 接收回复buffer[n] \0;std::cout Client received: buffer std::endl;close(sockfd);return 0;
}这里的sendto和recvfrom大家应该都懂主要是这个客户端是如何找到服务端的重要 sockaddr_in servaddr{};servaddr.sin_family AF_INET;servaddr.sin_port htons(12345);servaddr.sin_addr.s_addr inet_addr(127.0.0.1); // 本机测试在这段代码里的IP地址和端口号是找到服务端的关键 IP 地址 (127.0.0.1)告诉客户端要把数据发送到哪个机器。127.0.0.1 表示本机也就是服务端和客户端在同一台电脑。如果服务端在另一台电脑你就要写它的真实 IP例如 192.168.1.100。 端口号 (12345) 服务端在这个端口上监听数据。UDP/TCP 都是靠端口区分不同服务的就像房子门牌号一样。所以客户端通过 IP, port 就能找到服务端。 3-1 为什么客户端通常不需要手动定义 IP 和端口
客户端的职责是 主动找服务端在调用 sendto()时目标地址服务端的 IP端口 由程序员指定源地址客户端的 IP端口 不写时由内核自动分配
IP自动选择一块能到达服务端的本地网卡的 IP
端口自动分配一个 临时端口通常在 49152–65535 之间
客户端什么时候需要绑定 IP端口如果客户端希望 使用固定端口比如做 P2P、游戏服务器通知端口或者有多张网卡必须指定 用哪张网卡的 IP 去通信这种情况才会手动调用 bind()