动易网站首页错位,it行业软件开发,中国互联网协会会长,哪个网站是做安全教育前言
在现代社会#xff0c;网络技术已经成为我们日常生活和工作中不可或缺的一部分。从简单的网页浏览到复杂的分布式系统#xff0c;网络技术都扮演着至关重要的角色。通过这篇文章#xff0c;读者将从入门到精通#xff0c;全面掌握网络编程的理论和实践。
重点摘要
…前言
在现代社会网络技术已经成为我们日常生活和工作中不可或缺的一部分。从简单的网页浏览到复杂的分布式系统网络技术都扮演着至关重要的角色。通过这篇文章读者将从入门到精通全面掌握网络编程的理论和实践。
重点摘要
网络编程是一个复杂且多样的领域本节将重点关注以下几个方面
理解应用层的作用初识HTTP协议应用层是网络协议栈的顶层它负责处理特定的应用程序数据。理解传输层的作用深入理解TCP的各项特性和机制传输层在网络协议栈中负责数据传输的可靠性和准确性。对整个TCP/IP协议有系统的理解TCP/IP协议是互联网的基础理解它的工作原理是学习网络编程的关键。对TCP/IP协议体系下的其他重要协议和技术有一定的了解除了TCP和UDP还有许多其他重要的协议如HTTP、FTP、DNS等。学会使用一些分析网络问题的工具和方法如Wireshark、tcpdump等工具可以帮助我们分析和解决网络问题。
这些内容不仅是网络编程的理论基础也是服务器开发程序员的重要基本功更是各大公司笔试面试的核心考点。
应用层
应用层是网络协议栈的顶层负责处理特定的应用程序数据。我们日常使用的许多网络应用程序如浏览器、邮件客户端、聊天软件等都是在应用层运行的。
再谈“协议”
协议是一种“约定”是通信双方遵循的规则和标准。在网络编程中socket API提供了读写数据的接口这些接口通常以“字符串”的方式来发送接收数据。但如果我们需要传输“结构化的数据”怎么办呢
网络版计算器
例如我们需要实现一个服务器版的加法器。客户端需要发送两个加数服务器进行计算后再返回结果给客户端。我们可以采用以下两种方案
方案一
客户端发送一个形如“11”的字符串字符串中有两个操作数都是整形两个数字之间有一个字符是运算符运算符只能是“”数字和运算符之间没有空格
这种方法的优点是实现简单但缺点是扩展性差。如果需要增加其他运算符或支持浮点数计算就需要修改协议和解析逻辑。
方案二
定义结构体来表示交互的信息发送数据时将结构体按照一定规则转换成字符串接收数据时再按照相同规则将字符串转化回结构体这个过程叫做“序列化”和“反序列化”。
// proto.h 定义通信的结构体
typedef struct Request {int a;int b;
} Request;typedef struct Response {int sum;
} Response;// client.c 客户端核心代码
Request request;
Response response;scanf(%d,%d, request.a, request.b);
write(fd, request, sizeof(Request));
read(fd, response, sizeof(Response));// server.c 服务端核心代码
Request request;
read(client_fd, request, sizeof(request));
Response response;
response.sum request.a request.b;
write(client_fd, response, sizeof(response));这种方法的优点是扩展性强可以很容易地增加新的字段和功能但缺点是需要额外的序列化和反序列化过程增加了编程复杂度。
无论采用哪种方案只要保证一端发送的数据能够在另一端正确解析就可以认为协议是成功的。这种约定就是应用层协议。
HTTP协议
虽然应用层协议是我们程序员自己定的但实际上已经有很多前辈定义了一些现成的、非常好用的应用层协议供我们直接使用。HTTP超文本传输协议就是其中之一。
认识URL
平时我们俗称的“网址”其实就是URLUniform Resource Locator统一资源定位符。URL是用来标识互联网上的资源的地址。一个完整的URL包括以下几个部分
协议如http、https、ftp等主机名如www.example.com端口号默认为80HTTP或443HTTPS但可以指定其他端口路径如/index.html表示资源在服务器上的位置查询参数如?nameJohnage30用于传递额外的信息片段标识符如#section1用于标识页面内的特定位置。
urlencode和urldecode
在URL中某些字符如“/”、“?”、“:”等有特殊意义因此不能随意出现。如果某个参数需要包含这些特殊字符就必须先对其进行转义。转义规则如下
将需要转码的字符转为16进制然后从右到左取4位不足4位直接处理每2位做一位前面加上%编码成%XY格式。例如“”被转义成了“%2B”。
import urllib.parseoriginal_string Hello World!
encoded_string urllib.parse.quote(original_string)
decoded_string urllib.parse.unquote(encoded_string)print(Original:, original_string)
print(Encoded:, encoded_string)
print(Decoded:, decoded_string)urldecode就是urlencode的逆过程用于将转义后的字符还原成原始字符。这个过程在实际应用中非常常见特别是在处理GET请求的参数时。
HTTP协议格式
HTTP请求和响应的格式包括三部分首行、Header和Body。
HTTP请求
首行[方法] [URL] [版本]例如“GET /index.html HTTP/1.1”Header请求的属性以冒号分割的键值对每组属性之间用\n分隔遇到空行表示Header部分结束Body空行后面的内容都是BodyBody允许为空字符串如果存在Body则在Header中会有一个Content-Length属性来标识Body的长度。
HTTP响应
首行[版本号] [状态码] [状态码解释]例如“HTTP/1.1 200 OK”Header响应的属性以冒号分割的键值对每组属性之间用\n分隔遇到空行表示Header部分结束Body空行后面的内容都是BodyBody允许为空字符串如果存在Body则在Header中会有一个Content-Length属性来标识Body的长度。
HTTP的方法
HTTP定义了一系列方法用于指定对资源执行的操作。常见的方法包括
GET请求获取指定资源通常用于获取网页内容POST向指定资源提交数据进行处理常用于提交表单数据PUT向指定资源位置上传最新内容常用于更新资源DELETE删除指定资源常用于删除服务器上的资源HEAD类似GET方法但只请求资源的头部信息不返回实际内容OPTIONS请求指定资源的通信选项和需求通常用于跨域请求的预检PATCH对指定资源进行部分修改常用于更新资源的一部分。
HTTP的状态码
HTTP状态码用于表示服务器对请求的处理结果。常见的状态码包括
1xx信息性状态码 100 Continue客户端应继续请求。 2xx成功状态码 200 OK请求成功。201 Created请求成功并创建了新的资源。 3xx重定向状态码 301 Moved Permanently资源永久移动到新位置。302 Found资源临时移动到新位置。 4xx客户端错误状态码 400 Bad Request请求无效。401 Unauthorized未授权。403 Forbidden禁止访问。404 Not Found请求的资源不存在。 5xx服务器错误状态码 500 Internal Server Error服务器内部错误。502 Bad Gateway无效网关。503 Service Unavailable服务不可用。
HTTP常见Header
Header是HTTP请求和响应的重要组成部分用于传递请求和响应的元数据。常见的HTTP Header包括
Content-Type表示数据类型如text/html、application/json等Content-Length表示Body的长度Host客户端告知服务器请求的资源在哪个主机的哪个端口上User-Agent声明用户的操作系统和浏览器版本信息Referer当前页面是从哪个页面跳转过来的Location搭配
3xx状态码使用告诉客户端接下来要去哪里访问
Cookie用于在客户端存储少量信息通常用于实现会话session的功能。
User-Agent里的历史故事User-Agent最早是用于表示浏览器的版本信息但随着浏览器的更新换代这个字段也变得越来越复杂。例如早期的浏览器如Netscape和Internet Explorer都有各自的User-Agent字符串但随着时间的推移许多浏览器开始模仿这些字符串以提高兼容性。
最简单的HTTP服务器
实现一个最简单的HTTP服务器只在网页上输出“hello world”。只要我们按照HTTP协议的要求构造数据就很容易实现。
#include sys/socket.h
#include netinet/in.h
#include arpa/inet.h
#include unistd.h
#include stdio.h
#include string.h
#include stdlib.hvoid Usage() {printf(usage: ./server [ip] [port]\n);
}int main(int argc, char* argv[]) {if (argc ! 3) {Usage();return 1;}int fd socket(AF_INET, SOCK_STREAM, 0);if (fd 0) {perror(socket);return 1;}struct sockaddr_in addr;addr.sin_family AF_INET;addr.sin_addr.s_addr inet_addr(argv[1]);addr.sin_port htons(atoi(argv[2]));int ret bind(fd, (struct sockaddr*)addr, sizeof(addr));if (ret 0) {perror(bind);return 1;}ret listen(fd, 10);if (ret 0) {perror(listen);return 1;}for (;;) {struct sockaddr_in client_addr;socklen_t len;int client_fd accept(fd, (struct sockaddr*)client_addr, len);if (client_fd 0) {perror(accept);continue;}char input_buf[1024 * 10] {0}; // 用一个足够大的缓冲区直接把数据读完。ssize_t read_size read(client_fd, input_buf, sizeof(input_buf) - 1);if (read_size 0) {return 1;}printf([Request] %s, input_buf);char buf[1024] {0};const char* hello h1hello world/h1;sprintf(buf, HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s, strlen(hello), hello);write(client_fd, buf, strlen(buf));}return 0;
}备注
此处使用9090端口号启动了HTTP服务器。虽然HTTP服务器一般使用80端口但这只是一个通用的习惯并不是说HTTP服务器就不能使用其他的端口号。使用Chrome测试我们的服务器时可以看到服务器打出的请求中还有一个“GET /favicon.ico HTTP/1.1”这样的请求。读者可以自行查找资料理解favicon.ico的作用。
实验把返回的状态码改成404、403、504等观察浏览器上的效果。
传输层
传输层负责确保数据能够从发送端可靠地传输到接收端。传输层的两个主要协议是TCP和UDP。
再谈端口号
端口号Port标识了一个主机上进行通信的不同应用程序。在TCP/IP协议中用“源IP”、“源端口号”、“目的IP”、“目的端口号”、“协议号”这样一个五元组来标识一个通信。
端口号范围划分
0 - 1023知名端口号HTTP、FTP、SSH等这些广为使用的应用层协议的端口号都是固定的。1024 - 65535操作系统动态分配的端口号客户端程序的端口号由操作系统从这个范围分配。
认识知名端口号Well-Known Port Number
有些服务器非常常用为了使用方便人们约定一些常用服务器使用以下固定的端口号
SSH服务器使用22端口FTP服务器使用21端口Telnet服务器使用23端口HTTP服务器使用80端口HTTPS服务器使用443端口。
我们自己写程序使用端口号时要避开这些知名端口号。
两个问题
一个进程是否可以bind多个端口号一个端口号是否可以被多个进程bind
这些问题可以通过实验和查阅相关文档来验证。
netstat
netstat是一个用来查看网络状态的重要工具。
常用选项
n拒绝显示别名能显示数字的全部转化成数字l仅列出有在Listen监听的服务状态p显示建立相关链接的程序名t仅显示TCP相关选项u仅显示UDP相关选项a显示所有选项默认不显示LISTEN相关。
pidof
pidof是一个在查看服务器的进程ID时非常方便的工具。
语法pidof [进程名] 功能通过进程名查看进程ID。
UDP协议
UDPUser Datagram Protocol用户数据报协议是一种无连接、不可靠的传输层协议。
UDP协议端格式
UDP协议头包括以下字段
源端口号16位表示发送数据的应用程序的端口号目的端口号16位表示接收数据的应用程序的端口号长度16位表示整个UDP数据报的长度包括UDP头和数据部分校验和16位用于错误检测。
UDP的特点
无连接知道对端的IP和端口号就可以直接进行传输不需要建立连接不可靠没有确认机制和重传机制如果因为网络故障数据无法到达对方UDP协议层不会返回任何错误信息面向数据报不能灵活控制读写数据的次数和数量应用层交给UDP多长的报文UDP原样发送既不会拆分也不会合并。
UDP的缓冲区
UDP没有真正意义上的发送缓冲区。调用sendto会直接交给内核由内核将数据传给网络层协议进行后续的传输动作UDP具有接收缓冲区但不能保证收到的UDP报文的顺序和发送顺序一致。如果缓冲区满了再到达的UDP数据就会被丢弃。
UDP使用注意事项
UDP协议首部中的最大长度为16位即一个UDP报文的最大长度是64K包含UDP首部。如果需要传输的数据超过64K就需要在应用层手动分包多次发送并在接收端手动拼装。
基于UDP的应用层协议
NFS网络文件系统TFTP简单文件传输协议DHCP动态主机配置协议BOOTP启动协议用于无盘设备启动DNS域名解析协议
TCP协议
TCPTransmission Control Protocol传输控制协议是一种面向连接、可靠的传输层协议。
TCP协议段格式
TCP协议头包括以下字段
源/目的端口号16位表示数据从哪个进程来到哪个进程去序号32位用于标识数据段的顺序确认号32位表示已成功接收的数据段序号头部长度4位表示TCP头部的长度标志位6位包括URG、ACK、PSH、RST、SYN、FIN窗口大小16位用于流量控制校验和16位用于错误检测紧急指针16位标识紧急数据的位置选项可选字段用于扩展TCP功能。
确认应答ACK机制
TCP将每个字节的数据都进行了编号即为序列号。每一个ACK都带有对应的确认序列号告诉发送者哪些数据已被接收下一次从哪里开始发送。
超时重传机制
如果发送的数据在特定时间内没有收到确认应答发送端会进行重发。这个时间间隔根据网络环境动态计算。
连接管理机制
TCP要经过三次握手建立连接四次挥手断开连接。连接建立和断开的具体流程如下
服务端状态转换
CLOSED - LISTEN服务器端调用listen后进入LISTEN状态等待客户端连接LISTEN - SYN_RCVD监听到连接请求后进入SYN_RCVD状态并向客户端发送SYN确认报文SYN_RCVD - ESTABLISHED收到客户端确认报文后进入ESTABLISHED状态可以进行数据读写ESTABLISHED
- CLOSE_WAIT客户端主动关闭连接服务器收到结束报文段后进入CLOSE_WAIT状态
CLOSE_WAIT - LAST_ACK服务器调用close关闭连接后发送FIN报文进入LAST_ACK状态等待客户端确认LAST_ACK - CLOSED收到客户端的确认后彻底关闭连接。
客户端状态转换
CLOSED - SYN_SENT客户端调用connect发送同步报文段SYN_SENT - ESTABLISHEDconnect调用成功后进入ESTABLISHED状态开始数据读写ESTABLISHED - FIN_WAIT_1客户端主动调用close时向服务器发送结束报文段进入FIN_WAIT_1FIN_WAIT_1 - FIN_WAIT_2收到服务器确认后进入FIN_WAIT_2等待服务器的结束报文段FIN_WAIT_2 - TIME_WAIT收到服务器的结束报文段后进入TIME_WAIT发送最后的ACKTIME_WAIT - CLOSED等待2MSLMax Segment Life报文最大生存时间后进入CLOSED状态。
理解TIME_WAIT状态
TCP协议规定主动关闭连接的一方要处于TIME_WAIT状态等待两个MSL的时间后才能回到CLOSED状态。这是为了保证最后一个报文可靠到达防止重启服务器时收到错误的迟到数据。
解决TIME_WAIT状态引起的bind失败的方法
在某些情况下服务器需要处理大量客户端连接每个连接的生存时间很短但连接请求频繁。此时可以使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1允许创建端口号相同但IP地址不同的多个socket描述符。
理解CLOSE_WAIT状态
如果服务器上出现大量CLOSE_WAIT状态说明服务器没有正确关闭socket导致四次挥手没有正确完成。只需要在合适的位置加上close()即可解决问题。
滑动窗口
滑动窗口机制允许发送端在未收到确认应答前发送多个数据段大大提高了传输效率。窗口大小表示无需等待确认应答而可以继续发送数据的最大值。窗口越大网络的吞吐率越高。
流量控制
接收端通过TCP头部中的窗口大小字段告知发送端当前缓冲区的剩余空间发送端根据窗口大小调整发送速度防止缓冲区溢出。
拥塞控制
TCP引入慢启动机制在不清楚网络状态的情况下先发少量数据探测逐步增加发送量。拥塞窗口cwnd初始为1每次收到ACK后增加1超过慢启动阈值后按线性增长。
延迟应答和捎带应答
延迟应答通过稍微延迟ACK的发送时间增加窗口大小提高传输效率。捎带应答则利用应用层的响应数据捎带ACK一起发送。
面向字节流
TCP连接在内核中维护一个发送缓冲区和接收缓冲区数据在缓冲区中进行处理保证传输的可靠性和顺序性。TCP连接是全双工的既可以读数据也可以写数据。
粘包问题
粘包问题是指应用层看到的一串连续字节数据中无法区分出每个完整的数据包。解决方法包括
对于定长包按照固定大小读取对于变长包在包头约定一个包总长度字段或使用明确的分隔符。
TCP异常情况
进程终止释放文件描述符发送FIN正常关闭连接。机器重启与进程终止相同。机器掉电/网线断开接收端发现连接已断开进行重置或通过保活定时器释放连接。
TCP小结
TCP通过一系列复杂的机制保证传输的可靠性和高效性。这些机制包括校验和、序列号、确认应答、超时重发、连接管理、流量控制和拥塞控制等。
基于TCP的应用层协议
HTTPHTTPSSSHTelnetFTPSMTP
当然还包括我们自己定义的应用层协议。
TCP/UDP对比
TCP和UDP各有优缺点适用于不同的场景。TCP适用于可靠传输如文件传输和重要状态更新UDP适用于对高速传输和实时性要求较高的通信如视频传输和广播。
用UDP实现可靠传输经典面试题
参考TCP的可靠性机制在应用层实现类似的逻辑包括序列号、确认应答和超时重传等。
TCP相关实验
通过实验理解listen的第二个参数、使用Wireshark分析TCP通信流程等进一步掌握TCP协议的工作原理和实际应用。
理解 listen 的第二个参数
在TCP服务器编程中listen函数的第二个参数表示监听队列的最大长度。这个参数的值决定了内核为该套接字维护的两个队列的长度
半连接队列SYN Queue存放处于SYN_RECV状态的连接请求。全连接队列Accept Queue存放已完成三次握手但应用程序尚未调用accept取走的连接。
当一个新的连接请求到达时内核首先将其放入半连接队列。如果客户端发送的SYN报文被确认连接进入全连接队列。如果全连接队列已满新的连接请求将被拒绝。
#include tcp_socket.hppint main(int argc, char* argv[]) {if (argc ! 3) {printf(Usage ./test_server [ip] [port]\n);return 1;}TcpSocket sock;bool ret sock.Bind(argv[1], atoi(argv[2]));if (!ret) {return 1;}ret sock.Listen(2);if (!ret) {return 1;}// 客户端不进行 acceptwhile (1) {sleep(1);}return 0;
}使用 Wireshark 分析 TCP 通信流程
Wireshark是一个强大的网络协议分析工具可以抓取和分析网络通信数据包。以下是使用Wireshark分析TCP通信流程的步骤
下载和安装 Wireshark
可以从Wireshark官方网站下载并安装最新版本的Wireshark。
启用 telnet 客户端
在Windows上启用telnet客户端参考百度经验。
启动 Wireshark 并设置过滤器
启动Wireshark后在过滤器栏中输入ip.addr [服务器 ip]或tcp.port 9090以只抓取指定IP或端口的数据包。
观察三次握手过程
启动服务器后使用telnet客户端连接服务器抓取数据包并观察三次握手过程。可以看到三个报文各自的序列号和确认序号的规律。
telnet [ip] [port]观察确认应答
在telnet中输入一个字符可以看到客户端发送一个长度为1字节的数据服务器返回ACK和响应数据然后客户端反馈ACK。
观察四次挥手
在telnet中输入ctrl ]回到控制界面输入quit退出可以观察四次挥手的过程。
结语
网络基础是现代计算机科学的重要组成部分掌握网络编程的理论和实践不仅有助于理解计算机系统的运行机制还能提高开发高性能、可靠网络应用的能力。希望本篇文章能帮助读者从入门到精通全面掌握网络基础知识。 嗯就是这样啦文章到这里就结束啦真心感谢你花时间来读。 觉得有点收获的话不妨给我点个赞吧 如果发现文章有啥漏洞或错误的地方欢迎私信我或者在评论里提醒一声~