系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
系列文章目录
前言
一、pandas是什么?
二、使用步骤
1.引入库
2.读入数据
总结
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:以下是本篇文章正文内容,下面案例可供参考
1,TCP/IP 协议分层结构总览(了解功能即可)
TCP/IP 模型共分为四层,从上到下依次为:
层级名称 | 主要协议 | 功能概要 |
---|---|---|
应用层 (Application Layer) | HTTP, FTP, DNS, SMTP 等 | 提供用户应用服务,定义应用数据的格式和交换规则 |
传输层 (Transport Layer) | TCP, UDP | 提供端到端的传输控制和错误校验,保证数据可靠或快速传输 |
网络层 (Internet Layer) | IP, ICMP, ARP | 实现跨网络通信,负责逻辑寻址和路由选择 |
网络接口层(或链路层) (Link Layer) | Ethernet, PPP, MAC等 | 管理物理连接,定义数据在本地网络中如何传输 |
1.1 各层功能详解
1.1.1 应用层(Application Layer)
功能职责:
(1)为用户提供网络应用服务
(2)定义应用数据的格式、编码、会话控制等
典型协议:
协议 | 功能说明 |
---|---|
HTTP | 网页浏览(超文本传输) |
FTP | 文件传输 |
SMTP | 邮件发送 |
POP3/IMAP | 邮件接收 |
DNS | 域名解析(将域名转换为IP地址) |
Telnet | 远程登录 |
PS:你在浏览器输入网址并回车,实际使用的是HTTP协议与服务器通信。
1.1.2 传输层(Transport Layer)
功能职责:
(1)端到端的通信控制
(2)建立/管理/终止连接
(3)数据分段、重组
(4)流量控制、差错检测与纠正
协议典型:
协议 | 特性 | 适用场景 |
---|---|---|
TCP | 面向连接、可靠、顺序、校验 | 网页、文件传输、邮件等 |
UDP | 无连接、不保证顺序或完整性 | 视频直播、DNS、语音等实时场景 |
PS:如果你下载文件,通常使用TCP;而如果在看直播,则往往用的是UDP。
1.1.3 网络层(Internet Layer)
功能职责:
(1)实现IP地址之间的通信
(2)路由寻址和转发
(3)分片与重组
典型协议:
协议 | 功能说明 |
---|---|
IP | 逻辑寻址与分包转发 |
ICMP | 网络错误/状态信息传递(如ping) |
ARP | 将 IP 地址解析为 MAC 地址 |
说明:
IP协议是核心,提供无连接的传输服务(不保证可靠性)
ICMP是网络层的“诊断助手”,如ping命令使用它
ARP在局域网中用于解决“IP->MAC”的问题
1.1.4 网络接口层(Link Layer)
又成链路层、主机到网络层(Host-to-Network Layer)
功能职责:
(1)实际的数据帧传输
(2)硬件地址识别(如MAC)
(3)帧校验、介质访问控制等
典型协议/技术:
技术或协议 | 功能说明 |
---|---|
Ethernet | 以太网,局域网最常用的链路协议 |
PPP | 点对点协议,多用于拨号接入 |
WLAN/Wi-Fi | 无线局域网 |
MAC 协议 | 媒体访问控制,控制谁可发数据 |
PS:数据包经过这一层,被封装成帧并在实际网线上(或无线)传输
1.2 整体数据传输过程示意
以一个网页请求为例(如访问 https://www.baidu.com):
(1)应用层(HTTP):构造网页请求
(2)传输层(TCP):将请求封装成可靠的数据段
(3)网络层(IP):决定如何将数据段送往目标服务器的IP地址
(4)链路层(以太网/Wi-Fi):将数据帧通过物理媒介发送出去
服务器响应时则逆向传输回来。
1.3 总结与记忆建议
层级 | 关键词 | 功能一句话记忆 |
---|---|---|
应用层 | 用户交互 | 用户与网络的接口(让程序会说话) |
传输层 | 端到端通信 | 保证数据“准时准量”到对方(握手或快传) |
网络层 | 寻址与路由 | 找到目的 IP 的路径(决定去哪儿) |
网络接口层 | 局域链路传输 | 数据如何走“最后一公里”(怎么走、给谁送) |
2,什么是Socket API?
Socket(套接字)是应用层与传输层之间通信的编程接口,本质上是一个通信端点。Socket编程允许我们在用户空间使用TCP或UDP协议,进行网络通信。
TCP/IP 协议栈中的 传输层(TCP/UDP)被Socket抽象为统一接口,因此不同协议的通信流程具有高度一致性,区别在于细节控制。
2.1 Socket 编程核心 API(UNIX/Linux平台)
下表是主要的 socket API 函数及其用途:
函数 | 说明 | 适用于 |
---|---|---|
socket() |
创建 socket 对象 | TCP/UDP |
bind() |
绑定本地地址和端口 | TCP/UDP |
listen() |
监听连接请求(TCP 专用) | TCP |
accept() |
接受连接请求,返回新的 socket(TCP) | TCP |
connect() |
主动连接服务器 | TCP |
send() /recv() |
发送和接收数据 | TCP |
sendto() /recvfrom() |
发送和接收数据(指定目标地址) | UDP |
close() |
关闭 socket | TCP/UDP |
2.2 TCP Socket 编程流程
TCP服务器端流程图:
TCP客户端流程图:
2.3 TCP示例代码
2.3.1 TCP服务端(C语言示例)
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sd = {0,};
sd.sin_family = AF_INET;
sd.sin_addr.s_addr = htnol(INADDR_ANY);
sd.sin_port = htons(12345);
bind(sockfd, (struct sockaddr *)&sd, sizeof(sd));
listen(sockfd, 5);
int connfd = accept(sockfd, NULL, NULL);
char buffer[1024] = {0};
recv(connfd, buffer, sizeof(buffer), 0);
send(connfd, "Hello from server", 17, 0);
close(connfd);
close(sockfd);
2.3.2 TCP客户端(C语言示例)
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in sd = {0, };
sd.sin_family = AF_INET;
sd.sin_port = htons(12345);
inet_pton(AF_INET, "1237.0.0.1", &sd.sin_addr);
connect(sockfd, (struct sockaddr *)&sd, sizeof(sd));
send(sockfd, "Hello from client", 17, 0);
char buffer[1024] = {0, };
recv(sockfd, buffer, sizeof(buffer), 0);
close(sockfd);
2.4 UDP socket编程流程
UDP服务器端流程:
UDP客户端流程:
2.5 UDP示例代码
2.5.1 UDP服务端
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in serd = {0, }, clid = {0, };
skcklen_t len = sizeof(clid);
serd.sin_family = AF_INET;
serd.sin_addr.s_addr = htonl(INADDR_ANY);
serd.sin_port = hton(12345);
bind(sockfd, (struct sockaddr *)&serd, sizeof(serd));
char buffer[1024] = {0, };
recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&clid, &len);
sendto(sockfd, "UDP server reply", 16, 0, (struct sockaddr *)clid, &len);
close(sockfd);
2.5.2 UDP客户端
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
structt sockaddr_in serd = {0, };
serd.sin_family = AF_INET;
serd.sin_prot = htons(12345);
inet_pton(AF_INET, "127.0.0.1", &serd.sin_addr);
sendto(sockfd, "UDP client hello", 17, 0, (sturct sockaddr *)&serd, sizeof(serd));
char buffer[1024] = {0, };
recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
close(sockfd);
2.6 TCP vs UDP 对比表格
特性 | TCP | UDP |
---|---|---|
是否连接 | 有连接(可靠) | 无连接(不可靠) |
数据传输单位 | 字节流(stream) | 报文(datagram) |
是否保证顺序 | 是 | 否 |
是否保证可靠性 | 是(有确认、重传、流控、拥塞控制) | 否 |
适合场景 | 文件传输、网页、邮件等 | 实时语音、视频、DNS、NTP、广播等 |
2.7 Socket 编程核心函数原型与参数说明
1. int socket(int domain, int type, int protocol);
作用:创建一个套接字
参数:
domain:协议族,如 AF_INET(IPv4)或 AF_INET6
type:套接字类型,如 SOCK_STREAM(TCP),SOCK_DGRAM(UDP)
protocol:通常设为 0(自动选择),或指定 IPPROTO_TCP / IPPROTO_UDP
2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:将 socket 绑定到本地地址(IP + 端口)
参数:
sockfd:由 socket() 返回的描述符
addr:本地地址结构体指针,如 struct sockaddr_in
addrlen:地址结构体的长度(sizeof(struct sockaddr_in))
3. int listen(int sockfd, int backlog);
作用:监听指定 socket,使其成为被动连接等待状态(仅用于 TCP)
参数:
sockfd:由 socket() 返回的描述符
backlog:等待连接队列的最大长度
4. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
作用:从连接队列中取出一个连接(TCP 专用)
参数:
sockfd:监听 socket 的描述符
addr:保存客户端地址信息的结构体指针
addrlen:输入输出参数,指示地址结构体长度
5. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
作用:客户端发起连接请求(TCP)
参数:
sockfd:由 socket() 返回的描述符
addr:服务器地址结构体指针
addrlen:地址结构体长度
6. ssize_t send(int sockfd, const void *buf, size_t len, int flags);
作用:向已连接的 socket 发送数据(TCP)
参数:
sockfd:连接 socket 的描述符
buf:指向发送数据的缓冲区
len:缓冲区长度
flags:通常为 0,可指定 MSG_DONTWAIT 等选项
7. ssize_t recv(int sockfd, void *buf, size_t len, int flags);
作用:接收来自已连接 socket 的数据(TCP)
参数:
sockfd:连接 socket 的描述符
buf:存储接收数据的缓冲区
len:缓冲区大小
flags:通常为 0,可设为非阻塞标志等
8. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
作用:向指定目标发送数据(UDP)
参数:
sockfd:socket 描述符
buf:要发送的数据
len:数据长度
flags:发送选项
dest_addr:目标地址结构体指针
addrlen:地址结构体长度
9. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
作用:接收来自任意发送者的数据(UDP)
参数:
sockfd:socket 描述符
buf:接收缓冲区
len:缓冲区长度
flags:通常为 0
src_addr:保存发送方地址的结构体指针
addrlen:输入输出参数,表示地址结构体大小
10. int close(int fd);
作用:关闭 socket 文件描述符(释放资源)
参数:
fd:socket 描述符
11. int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
作用:设置 socket 选项(如复用地址、超时时间等)
参数:
sockfd:socket 描述符
level:选项级别,如 SOL_SOCKET
optname:选项名称,如 SO_REUSEADDR
optval:选项值指针
optlen:选项值的长度
12. int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
作用:获取 socket 当前设置的选项值
参数:同上
3, 常见套接字属性设置(使用 setsockopt();)
套接字选项可分为几个维度:
属性类型 | 常用选项名 | 设置作用 |
---|---|---|
地址重用 | SO_REUSEADDR , SO_REUSEPORT |
允许端口复用,避免 bind 失败 |
缓冲区大小 | SO_RCVBUF , SO_SNDBUF |
设置接收、发送缓冲区大小 |
保活机制 | SO_KEEPALIVE |
检测连接是否断开 |
延迟控制 | TCP_NODELAY (TCP专用) |
禁用 Nagle 算法,提升实时性 |
超时设置 | SO_RCVTIMEO , SO_SNDTIMEO |
设置 I/O 操作超时时间 |
广播权限 | SO_BROADCAST (UDP 专用) |
启用 UDP 广播能力 |
Linger 机制 | SO_LINGER |
设置 close() 行为(是否阻塞等待发送) |
3.1 setsockopt()原型
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
sockfd: 套接字描述符
level: 设置哪个协议层,如 SOL_SOCKET、IPPROTO_TCP
optname: 选项名称
optval: 选项值指针
optlen: 选项值长度
3.2 关键属性设置详解与示例
3.2.1 设置端口快速复用:SO_REUSEADDR
int prots = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
作用:允许多个socket在TIME_WAIT状态时立即重用同一端口
建议:服务端监听前调用,防止 “Address already in use” 报错
3.2.2 设置TCP禁用Nagle算法:TCP_NODELAY
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
作用: 提升小包发送实时性(适合游戏、音视频)
风险:过度禁用可能造成小包碎片增加
3.2.3 启用TCP保活机制:SO_KEEPALIVE
int keepalive = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepakive));
默认内核探测策略可通过如下参数设置(单位:秒)
#启动保活时间(无数据后多久开始探测)
echo 60 > /proc/sys/net/ipv4/tcp_keepalive_time
#探测间隔
echo 5 > /proc/sys/net/ipv4/tcp_keepalive_intvl
#探测失败次数上限
echo 3 > /proc/sys/net/ipv4/tcp_keepalive_probes
3.2.4 设置超时时间(避免recv()、send()阻塞)
struct timeval timeout = {3, 0}; // 3s
setsockopt(sockfd, SOL_SOCKET, SO_ECVITMEO, &timeout, sizeof(timeout));
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));
建议:UDP常用,或TCP需要保障应用层超时控制
3.2.5 设置发送和接收缓冲区大小
int size = 64 * 1024; // 64KB
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
注意:
内核可能对大小有最小或最大限制,可用 getsockopt(); 反查实际值
3.2.6 启用UDP广播权限:SO_BROADCAST
int broadcast = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast));
用于发送 192.168.1.255 的广播报文
没有此设置时UDP广播 sendto()会失败(Permission denied)
3.2.7 设置 SO_LINGER 控制 close() 行为
struct linger lin;
lin.l_onoff = 1; // 启用 linger
lin.l_linger = 5; // 最多等5s发送完
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lin, sizeof(lin));
含义:
l_onoff = 0; 立即关闭(默认行为)
l_onoff = 1 && l_linger = 0; 强行关闭(发送RST)
l_onoff = 1 && l_linger > 0; 阻塞等待发送数据完成
3.2.8 建议组合(实际经验推荐)
应用场景 | 推荐设置 |
---|---|
高并发 TCP 服务器 | SO_REUSEADDR ,SO_RCVBUF/SO_SNDBUF ,SO_LINGER (延迟关闭) |
实时通信 TCP 客户端 | TCP_NODELAY ,SO_KEEPALIVE ,SO_RCVTIMEO |
UDP 广播或组播 | SO_BROADCAST ,SO_RCVBUF |
长连接稳定性保障 | SO_KEEPALIVE + 调整 /proc 中的相关参数 |
3.2.9 查看当前套接字属性
使用 getsockopt()查询:
int val = 0;
socklen_t len = szieof(val);
getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &val, &len);
printf("recv buf = %d
", val);
使用 ss 或 netstat 工具查看:
ss -tnp
netstat -anp
4,C/S架构定义与本质
C/S架构(Client/Server)是指系统由两个部分组成:
1,Client(客户端):发起请求,负责用户交互和业务逻辑处理。
2,Server(服务器):接收请求,处理并返回响应,集中处理数据、服务等。
4.1 核心特征
1,角色分工明确(发起 vs 响应)
2,通信基本标准协议(如 TCP/IP)
3,逻辑分层部署(客户端注重表现,服务端注重计算和存储)
4.2 C/S架构的通信流程图(TCP为例)
客户端 服务端
│ │
│-------- socket() 创建套接字 ---------------▶│
│ │
│----------- connect() 发起连接 -------------▶│
│ │
│ socket() + bind() + listen()
│ │
│◀---------- accept() 接受连接 ---------------│
│ │
│----------- send()/write() 发送数据 --------▶│
│ │
│◀---------- recv()/read() 接收数据 ----------│
│ │
│ (通信过程循环) │
│ │
│----------- close() 关闭连接 --------------▶│
│ │
4.3 C/S架构的组成模块
组件 | 客户端 | 服务器端 |
---|---|---|
网络层逻辑 | 创建 socket,连接服务端 | 创建 socket,绑定端口,监听连接 |
通信协议 | 通常使用 TCP 或 UDP | 同上 |
应用逻辑 | 发起请求、处理响应、展示结果 | 接收请求、执行业务、返回响应 |
多路并发 | 一般为单连接、短时交互 | 需支持并发处理(多线程/多进程/epoll) |
状态管理 | 连接状态由客户端主动维护 | 服务端可能维护连接会话信息 |
4.4 TCP与UDP的C/S架构对比
项目 | TCP(面向连接) | UDP(无连接) |
---|---|---|
连接建立 | 需三次握手 | 不建立连接,直接发送 |
数据可靠性 | 高,自动重传与校验 | 无保证,需应用层处理 |
编程复杂度 | 较高(需连接管理) | 较低(一次性发送) |
应用场景 | HTTP、数据库、SSH | DNS、视频流、语音、广播 |
4.5 最小可允许TCP/UDP、C/S模型,扩展多线程/epoll高并发
模型编号 | 通信类型 | 客户端实现方式 | 服务端实现方式 |
---|---|---|---|
① | TCP | 单连接 | 单连接 |
② | UDP | 无连接 | 无连接 |
③ | TCP | 单连接 | 多线程并发处理 |
④ | TCP | 单连接 | epoll 并发处理(Linux专用) |
4.5.1 ① TCP 单连接 C/S 示例代码
服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int mian(void)
{
int sfd, cfd;
struct sockaddr_in saddr, caddr;
char buffer[BUFFER_SIZE];
// 创建套接字
sfd = socket(AF_INFT, SOCK_STREAM, 0);
// 绑定地址
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr));
// 监听
listen(sfd, 5);
printf("Server listening on port %d...
", PORT);
socklen_t clen = sizeof(caddr);
cfd = accept(sfd, (struct sockaddr *)&caddr, &clen);
// 通信
while(1) {
memset(buffer, 0, BUFFER_SIZE);
int len = recv(cfd, buffer, BUFFER_SIZE -1, 0);
if (len <= 0) break;
printf("Receivd from client:%s
", buffer);
send(cfd, buffer, len, 0); // 回显
}
close(cfd);
close(sfd);
return 0;
}
客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main(void)
{
int sockfd;
struct sockaddr_in saddr;
char buffer[BUFFER_SIZE];
// 创建socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 配置服务器地址
saddr.sin_family = AF_INET;
saddr.sin_port = hton(PORT);
inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
// 连续服务器
connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
while(1) {
printf("Input message: ");
fgets(buffer, BUFFER_SZIE, stdin);
send(sockfd, buffer, strlen(buffer), 0);
memset(buffer, 0, BUFFER_SIZE);
recv(sockfd, buffer, BUFFER_SZIE -1, 0);
printf("Echo from server: %s
", buffer);
}
close(sockfd);
return 0;
}
4.5.2 ② UDP 无连接 C/S 模型
UDP是无连接的,客户端与服务端通过 sendto() 和 recvfrom() 直接收发数据,无需建立连接。
UDP服务端(udp_server.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main(void)
{
int sockfd;
struct sockaddr_in saddr, caddr;
char buffer[BUFFER_SIZE];
socklen_t addr_len = sizeof(caddr);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
saddr.sin_faily = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
printf("UDP Server listening on port %d...
", PORT);
while(1) {
memset(buffer, 0, BUFFER_SZIE);
int len = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0
(struct sockaddr *)&caddr, &addr_len);
if (len < 0) {
printf("Recvfrom error.");
continue;
}
printf("Received from %s:%d: %s
", inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port), buffer);
// 回显数据
sendto(sockfd, buffer, len, 0, (struct sockaddr *)&caddr, addr_len);
}
close(spckfd);
return 0;
}
UDP客户端(udp_client.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFER_SIZE 1024
int main(void)
{
int sockfd;
struct sockaddr_in saddr;
char buffer[BUFFER_SIZE];
socklen_t addr_len = sizeof(saddr);
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
while(1) {
printf("Input message: ");
fgets(buffer. BUFFER_SZIE, stdin);
snedto(sockfd, buffer, strlen(buffer), 0,
(struct sockaddr *)&saddr, addr_len);
memset(buffer, 0, BUFFER_SIZE);
int len = recvfrom(sockfd, buffer, BUFFER_SIZE -1, 0,
NULL, NULL);
if (len < 0) {
printf("Recvfrom error!
");
continue;
}
printf("Echo from server: %s
", buffer);
}
close(sockfd);
return 0;
}
4.5.3 ③ TCP 多线程服务端模型
服务端为每个连接创建独立线程,适合中小规模并发,便于理解
多线程TCP服务端(tcp_thread_server.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT 8888
#define BUFFER_SIZE 1024
void *client_handler(void *arg)
{
int cfd = *(int *)arg;
free(arg);
char buffer[BUFFER_SIZE];
while (1) {
memset(buffer, 0, BUFFER_SIZE);
int len = recv(cfd, buffer, BUFFER_SZIE - 1, 0);
if (len <= 0) break;
printf("Received: %s
", buffer);
send(cfd, buffer, len, 0); // 回显
}
close(cfd);
printf("Client disconnected
");
return NULL;
}
int main(void)
{
int sfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
bind(sfd, (struct sockkaddr *)&saddr, sizeof(saddr));
listen(sfd, 10);
printf("Multi-thread TCP Server listening on port %d...
", PORT);
while (1) {
struct sockaddr_in caddr;
socklen_t clen = sizeof(caddr);
int *cfd = malloc(sizeof(int));
*cfd = accept(sfd, (struct sockaddr *)&caddr, &clen);
if (*cfd < 0) {
printf("accept error
");
free(cfd);
continue;
}
pthread_t tid;
pthread_create(&tid, NULL, client_handler, cfd);
pthread_detach(tid); // 线程结束后自动回收资源
}
close(sfd);
return 0;
}
TCP 多线程客户端可用之前的tcp_client.c代码,无需修改
4.5.4 ④ TCP epoll 高性能服务端模型(Linux 专用)
epoll允许单线程处理海量连接,适合高性能场景
epoll TCP 服务端(tcp_epool_server.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#define PORT 8888
#define MAX_EVENTS 1000
#define BUFFER_SIZE 1024
// 设置文件描述符为非阻塞
int set_nonblocking(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags = -1) return -1;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int main(void)
{
int s_fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = INADDR_ANY;
bind(s_fd, (struct sockaddr *)&saddr, sizeof(saddr));
listen(s_fd, 128);
int epoll_fd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];
set_nonblocking(s_fd);
ev.events = EPOLLIN;
ev.data.fd = s_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s_fd, &ev);
printf("Epoll TCP Server listening on port %d...
", PORT);
while (1) {
int i;
int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
for (i = 0; i < nfds; i++) {
if (events[i].data.fd == s_fd) {
// 新连接
struct sockaddr_in caddr;
socklen_t c_len = sizeof(caddr);
int c_fd = accept(s_fd, (struct sockaddr *)&caddr, &c_len);
if (c_fd < 0) continue;
set_nonblocking(c_fd);
ev.events = EPOLLIN | EPOLLET; // 边缘触发
ev.data.fd = c_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, c_fd, &ev);
printf("New client connected: %s:%d
",
inet_ntoa(caddr.sin_addr),
ntohs(caddr.sin_port);
} else {
// 处理客户端数据
int c_fd = events[i].data.fd;
char buffer[BUFFER_SIZE];
int done = 0;
while(1) {
ssize_t count = read(c_fd. buffer, sizeof(buffer));
switch count{
case -1:
// 表示数据读完
break;
case 0:
// 表示连接关闭
done = 1;
break;
default:
done = 0;
break;
}
// 回显
write(c_fd, buffer, count);
}
if (done) {
printf("Client disconnected: fd %d
", c_fd);
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, c_fd, NULL);
close(c_fd);
}
}
}
}
close(s_fd);
close(epoll_fd);
return 0;
}
4.6 套接字函数原型与参数说明清单
4.6.1 套接字基础函数
函数 | 原型 | 参数说明 |
---|---|---|
socket() |
int socket(int domain, int type, int protocol); |
– domain : 地址族,如 AF_INET (IPv4)- type : 套接字类型,如 SOCK_STREAM 、SOCK_DGRAM – protocol : 协议编号,通常设为 0 (自动匹配) |
bind() |
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
– sockfd : 套接字文件描述符- addr : 绑定的地址结构体指针- addrlen : 结构体大小 |
listen() |
int listen(int sockfd, int backlog); |
– sockfd : 套接字描述符- backlog : 最大等待连接队列长度 |
accept() |
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
– sockfd : 监听套接字- addr : 客户端地址结构体- addrlen : 地址结构体长度 |
connect() |
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
– sockfd : 套接字- addr : 服务器地址- addrlen : 地址结构体大小 |
close() |
int close(int fd); |
– fd : 文件描述符 |
4.6.2 数据读写函数
TCP/UDP 通用
函数 | 原型 | 参数说明 |
---|---|---|
send() |
ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
– sockfd : 已连接的套接字- buf : 发送数据缓冲区- len : 数据长度- flags : 通常设为 0 |
recv() |
ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
– sockfd : 已连接的套接字- buf : 接收缓冲区- len : 缓冲区大小- flags : 通常设为 0 |
UDP专用
函数 | 原型 | 参数说明 |
---|---|---|
sendto() |
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
– dest_addr : 目标地址- 其余同上 |
recvfrom() |
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); |
– src_addr : 发送方地址存储结构体- addrlen : 地址结构体大小- 其余同上 |
4.6.3 线程相关函数(仅模型③)
函数 | 原型 | 参数说明 |
---|---|---|
pthread_create() |
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); |
– thread : 线程 ID 指针- attr : 属性,通常为 NULL – start_routine : 线程函数- arg : 传递给线程的参数 |
pthread_detach() |
int pthread_detach(pthread_t thread); |
– thread : 线程 ID,使其分离 |
4.6.4 epoll相关函数(仅模型④)
函数 | 原型 | 参数说明 |
---|---|---|
epoll_create1() |
int epoll_create1(int flags); |
– flags : 通常设为 0 ,或 EPOLL_CLOEXEC |
epoll_ctl() |
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); |
– epfd : epoll 实例描述符- op : 操作,如 EPOLL_CTL_ADD /DEL /MOD – fd : 目标描述符- event : 已关注的事件 |
epoll_wait() |
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); |
– epfd : epoll 实例- events : 返回的事件数组- maxevents : 数组长度- timeout : 等待时间(ms) |
fcntl() |
int fcntl(int fd, int cmd, ...); |
设置文件描述符为非阻塞时使用,如:fcntl(fd, F_SETFL, O_NONBLOCK); |
4.6.5 地址处理辅助函数
函数 | 原型 | 参数说明 |
---|---|---|
inet_pton() |
int inet_pton(int af, const char *src, void *dst); |
– 将 IP 字符串转换为网络字节序地址 |
inet_ntoa() |
char *inet_ntoa(struct in_addr in); |
– 将 in_addr 转换为字符串 IP |
htons() / ntohs() |
uint16_t htons(uint16_t hostshort); |
– 主机序/网络序转换(端口) |
htonl() / ntohl() |
uint32_t htonl(uint32_t hostlong); |
– 主机序/网络序转换(IP 地址) |
4.6.6 总结:函数原型一览表
函数类别 | 原型 |
---|---|
创建套接字 | int socket(int domain, int type, int protocol); |
绑定地址 | int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
监听连接 | int listen(int sockfd, int backlog); |
接受连接 | int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); |
发起连接 | int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); |
TCP 发送 | ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
TCP 接收 | ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
UDP 发送 | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); |
UDP 接收 | ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); |
关闭套接字 | int close(int fd); |
创建线程 | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); |
分离线程 | int pthread_detach(pthread_t thread); |
创建 epoll | int epoll_create1(int flags); |
控制 epoll | int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); |
等待事件 | int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); |
设置非阻塞 | int fcntl(int fd, int cmd, ...); |
地址转换 | int inet_pton(int af, const char *src, void *dst); |
地址转字符串 | char *inet_ntoa(struct in_addr in); |
主机/网络序转换 | uint16_t htons(uint16_t); , uint32_t htonl(uint32_t); , 等 |
5,多进程、多线程服务器模型
5.1 基本定义与背景
模型 | 定义概述 |
---|---|
多进程模型 | 为每个客户端连接创建一个独立进程,服务端进程通过 fork() 或 clone() 复制子进程来处理客户端请求 |
多线程模型 | 为每个客户端连接创建一个独立线程,在一个共享进程空间中通过 pthread_create() 启动新线程处理请求 |
5.2 模型结构与原理
5.2.1 多进程服务器模型
结构示意图:
+------------------+
| 主进程 (监听) |
+------------------+
|
+------+------+
| |
+-------+------+ +----+--------+
| 子进程 1 (处理) | | 子进程 N (处理) |
+--------------+ +---------------+
工作流程:
1,主进程创建监听 socket。
2,调用 accept() 等待连接。
3,每当有客户端连接到达,fork() 出一个子进程处理请求。
4,子进程与客户端通信,处理完后退出或回收。
5,主进程继续监听下一连接。
示例代码代码片段:
int connfd = accept(sockfd, ...);
pid_t pid = fork();
if(pid == 0) {
// 子进程
close(sockfd); // 子进程无需监听套接字
handle_client(connfd);
exit(0);
} else {
// 主进程
close(connfd); // 主进程无需与客户端通信
}
5.2.1.1 多进程服务模型具体代码示例
基于C语言,使用IPV4 + TCP + fork() 实现。此代码清晰展现:
1,基本socket流程(socket -> bind -> listen -> accept)
2,每个客户端连接创建独立子进程处理
3,主进程负责监听并继续接受新连接
4,使用 SIGCHLD 处理子进程回收,避免僵尸进程
多进程TCP服务器模型(完整可编译)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
#define PORT 8888
#define BACKLOG 10
#define BUFSIZE 1024
// SIGCHLD 信号处理函数,防止僵尸进程
void sigchld_handler(int sig)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// 子进程用于处理客户端通信
void handle_client(int connfd, struct sockaddr_in client_addr)
{
char buf[BUFSIZE];
ssize_t n;
printf("子进程 [%d]: 处理客户端 %s:%d
", getpid(),
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
while((n = recv(connfd, buf, BUFSIZE, 0)) > 0) {
buf[n] = '';
printf("收到客户端[%s:%d]消息:%s
", inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port), buf);
send(connfd, buf, n, 0); // 回显
}
printf("客户端[%s:%d]断开连接。
", inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port));
close(connfd);
exit(0);
}
int main(void)
{
int listenfd, connfd;
struct sockaddr_in s_addr, c_addr;
socklen_t addrlen = sizeof(c_addr);
// 注册信号处理函数
signal(SIGCHLD, sigchld_handler);
// 创建监听 socket
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("socket failed!
");
return 1;
}
// 地址复用设置,避免 TIME_WAIT 占用端口
int opt = 1;
setsockopt(listenfd, SOL_SOKET, SO_REUSEADDR, &opt, sizeof(opt));
// 配置服务器地址
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(PORT);
s_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定地址
if (bind(listenfd, (struct sockaddr *)*s_addr, sizeof(s_addr)) < 0) {
printf("Bind failed!
");
close(listenfd);
return 1;
}
// 监听端口
if (listen(listenfd, BACKLOG) < 0) {
printf("Listen failed!
");
return 1;
}
printf("服务器监听端口 %d,等待连接...
", PORT);
while(1) {
connfd = accept(listenfd, (struct sockaddr *)&c_addr, &addrlen);
if (connfd < 0) {
if (errno == EINTR) continue; // 被信号打断则重试
printf("accept failed!
");
continue;
}
// 创建子进程处理客户端
pid_t pid = fork();
if (pid == 0) {
// 子进程
close(listenfd); // 子进程不再监听新连接
handle_client(connfd, client_addr);
} else if (pid > 0) {
// 父进程
close(connfd); // 父进程不负责通信
} else {
printf("Fork failed!
");
close(connfd);
continue;
}
}
close(listenfd);
return 0;
}
使用 telent 或者 nc 连接测试:
telnet 127.0.0.1 8888
# 或者
nc 127.0.0.1 8888
技术要点回顾:
技术点 | 说明 |
---|---|
fork() |
为每个连接创建独立子进程 |
SIGCHLD |
捕获子进程退出信号并回收资源 |
SO_REUSEADDR |
允许快速重启服务器端口 |
close() |
父/子进程正确关闭不需要的 fd,防止 fd 泄漏 |
5.2.2 多线程服务模型
结构示意图:
+------------------+
| 主线程 (监听) |
+------------------+
|
+--------------+--------------+
| | |
+--------+----+ +-------+----+ +------+-----+
| 线程 1 (处理) | | 线程 2 (处理) | | 线程 N (处理) |
+-------------+ +--------------+ +--------------+
工作流程:
1,主线程创建监听 socket。
2,调用accept() 等待连接。
3,有连接时调用 pthread_create() 创建新线程。
4,线程执行通信逻辑。
5,线程执行完毕后可选择退出或重用。
示例核心代码片段:
int connfd = accept(sockfd, ...);
pthread_t tid;
pthread_create(&tid, NULL, handle_client, (void *)&connfd);
pthread_detach(tid); // 线程分离,自动回收资源
5.2.2.1 多线程服务器模型具体代码示例
多线程服务器模型的完整C语言代码示例,基于 pthread 库实现。此例完整展现了:
1,使用 pthread_create() 为每个客户端连接创建线程;
2,每个线程独立处理对应客户请求;
3,主线程持续接受新连接;
4,使用 detached 模式防止资源泄露
5,使用 SO_REUSEADDR 避免端口占用问题。
多线程TCP服务器完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#define PORT 8888
#define BACKLOG 10
#define BUFSIZE 1024
typedef struct c_info_st{
int connfd;
struct sockaddr_in c_addr;
} c_info_t;
// 线程处理函数
void *handle_client(void *arg)
{
c_info_t *client = (c_info_t *)arg;
char buf[BUFSIZE] = {0, };
ssize_t n;
printf("线程[%ld]处理客户端 %s:%d
", pthread_self(),
inet_ntoa(client->client_addr.sin_addr),
ntohs(client->client_addr.sin_port));
while((n = recv(client->connfd, buf, BUFSIZE, 0)) > 0) {
buf[n] = '';
printf("收到客户端 [%s:%d] 消息:%s
",
inet_ntoa(client->client_addr.sin_addr),
ntohs(client->client_addr.sin_port), buf);
send(client->conffd, buf, n, 0); // 回显
}
printf("客户端 [%s:%d] 断开连接.
", inet_ntoa(client->client_addr.sin_addr),
ntohs(client->client_addr.sin_port));
close(client->connfd);
free(client);
pthread_exit(NULL);
}
int main(void)
{
int s_fd, c_fd;
struct sockaddr_in s_addr, c_addr;
socklen_t addrlen = sizeof(c_addr);
// 创建socket
if ((s_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("Socket failed!
");
return 1;
}
// 设置地址复用
int opt = 1;
setsockopt(s_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// 绑定地址
memset(&s_addr, 0, sizeof(s_addr));
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(PORT);
s_aaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s_fd, (struct sockaddr *)&s_addr, sizeof(s_addr)) < 0) {
printf("Bind failed!
");
close(s_fd);
return 1;
}
// 监听连接
if (listen(s_fd, BACKLOG) < 0) {
printf("Listen failed!
");
close(s_fd);
return 1;
}
printf("服务器启动,监听端口 %d...
", PORT);
while(1) {
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &addrlen);
if (c_fd < 0) {
if (errno == EINTR) continue;
printf("Accept failed!
");
continue;
}
}
// 为每个连接动态分配 c_info 结构
c_info_t *client = malloc(sizeof(c_info_t));
if (client == NULL) {
printf("Malloc failed!
");
close(c_fd);
continue;
}
client->connfd = c_fd;
client->client_addr = c_addr;
pthread_t tid;
if (pthread_create(&tid, NULL, handle_client, (void *)client) != 0) {
printf("pthread_create failed!
");
close(c_fd);
free(client);
continue;
}
// 设置线程分离,避免资源泄露
pthread_detach(tid);
close(s_fd);
return 0;
}
技术要点解析:
关键点 | 说明 |
---|---|
pthread_create() |
创建独立线程处理连接 |
pthread_detach() |
设置线程分离,自动释放资源 |
malloc/free |
每个客户端连接都封装为动态结构传递给线程 |
recv/send |
回显服务 |
SO_REUSEADDR |
避免端口重复绑定错误 |
inet_ntoa() 、ntohs() |
将 IP/端口转换为可读格式 |
6,常见的网络协议分析:ping、 tftp、 ftp 等(只描述功能)
6.1 ping协议(ICMP)
原理概述:
1,ping命令基于 ICMP (Internet Control Message Protocol)工作;
2,属于 网络层协议,非TCP/UDP;
3,常用 ICMP Echo Request / Echo Reply 来测试目标主机是否可达
6.1.1 ICMP报文结构(部分字段)
字段 | 位数 | 说明 |
---|---|---|
Type | 8 | 8=Echo Request, 0=Echo Reply |
Code | 8 | 子类型,一般为 0 |
Checksum | 16 | 整个 ICMP 校验和 |
Identifier | 16 | 用于区分多个请求 |
Sequence No. | 16 | 每次发送递增 |
Data | 可变 | 可携带任意数据 |
6.2 TFTP (Trivial File Transfer Protocol)
原理概述:
1,一种极简的文件传输协议;
2,使用UDP,端口69;
3,适用于内嵌设备、PEX启动、小型路由器固件更新;
4,无需认证、无连接、只支持读/写文件。
6.2.1 报文结构(部分字段)
字段 | 长度 | 说明 |
---|---|---|
Opcode | 2 字节 | 操作码(1=RRQ, 2=WRQ, 3=DATA, 4=ACK, 5=ERROR) |
Filename | N 字节 | 请求文件名 |
Mode | N 字节 | octet(默认二进制)或 netascii |
6.3 FTP(File Transfer Protocol)
原理概述:
1,基于 TCP 的传统文件传输协议;
2,控制连接使用 TCP 21 端口;
3,数据传输连接使用 TCP 20 或动态端口;
4,分为主动模式和被动模式(影响NAT穿透);
5,支持用户认证、命令行操作、文件/目录浏览。
6.3.1 常用FTP命令
命令 | 说明 |
---|---|
USER / PASS | 用户登录 |
LIST | 列出目录 |
RETR / STOR | 下载/上传文件 |
PASV / PORT | 主动/被动模式切换 |
6.5 总结表格
协议 | 协议层 | 端口 | 传输层协议 | 用途 | 是否可靠 | 是否认证 |
---|---|---|---|---|---|---|
ICMP (ping) | 网络层 | 无 | 无(IP直接) | 网络测试 | ✘ | ✘ |
TFTP | 应用层 | 69 (UDP) | UDP | 简单文件传输 | ✘ | ✘ |
FTP | 应用层 | 21/20 (TCP) | TCP | 完整文件服务 | ✔ | ✔ |
7,Wireshark 抓包分析示例
以下我将以 ping、以及自配协议为例,提供:
1,抓包前的大准备与过滤语法
2,抓包后的典型报文结构截图分析
3,每一层协议的字段结构说明
4,关键字段的工程含义
7.1 wireshark抓包准备工作
1,启动抓包: sudo wireshark &
2,选择网卡接口:选择有流量的接口(如eth0, ens33, wlan0)
3,使用过滤器
场景 | 过滤器语法 |
---|---|
捕捉 ping 包 | icmp |
捕捉 TFTP | udp.port == 69 |
捕捉 FTP 控制连接 | tcp.port == 21 |
只看某 IP | ip.addr == 192.168.1.100 |
同时多个条件 | `icmp |
7.2 抓包示例与字段分子
7.2.1 示例1:ping 抓包(ICMP)
抓包画面简析:
Frame 1: 98 bytes
Ethernet II
Internet Protocol Version 4
Internet Control Message Protocol
Type: 8 (Echo Request)
Code: 0
Checksum: 0x3a4f
Identifier: 0x1c46
Sequence number: 1
字段解析(ICMP):
字段 | 值示例 | 含义 |
---|---|---|
Type | 8 |
Echo Request(请求) |
Code | 0 |
子类型,固定 |
Identifier | 0x1c46 |
每个 ping 会生成一个标识符 |
Seq Number | 1 |
每次 ping 增加,便于匹配应答 |
Data | … | 实际传输数据(默认 56 字节) |
工程意义:
1,请求有请求 ID 和序列号,用于区分多个并发 ping
;
2,若出现 Unreachable / Timeout,ICMP 会返回错误类型(如 Type 3
Destination Unreachable);
3,TTL
字段可用于判断主机跳数。
7.2.2 自配协议
通用场景下:使用 wireshark 抓包 “原始数据” + 手工分析
如果您的协议是基于 TCP / UDP / 原始 IP 层实现的(大多数都是),那么您可以直接使用 Wireshark 进行抓包,例如:
示例:
假设你定义了一个TCP协议:
自定义协议报文格式:
[2字节 magic] [1字节 type] [4字节 payload_len] [payload 数据]
然后使用 wireshark 抓包 tcp.proto == 9000,得到如下十六进制内容:
0000 ab cd 01 00 00 00 0a 48 65 6c 6c 6f 57 6f 72 6c
0001 64
手动分析过程如下:
偏移 | 字段 | 内容 | 说明 |
---|---|---|---|
0x00 | magic | ab cd | 魔术字,标识协议 |
0x02 | type | 0x01 | 消息类型:例如登录 |
0x03 | payload_len | 00 00 00 0a | 长度 10 |
0x07 | payload | HelloWorld | 实际数据内容 |
网络编程方面完结!!!
一、pandas是什么?
示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。
二、使用步骤
1.引入库
代码如下(示例):
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
2.读入数据
代码如下(示例):
data = pd.read_csv(
'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
该处使用的url网络请求的数据。
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
暂无评论内容