【网络通信】socket和websocket知识整理
在网络通信中,我们可能会常常用到socket编程、websocket编程、HTTP通信、TCP/IP等,那它们是如何通信,有什么联系呢,下面简要总结了它们的原理、联系以及区别,可能会有理解错误的地方,望大家指出~
在网络通信中,我们可能会常常用到socket编程、websocket、HTTP通信、TCP/IP等,那它们是如何通信,有什么联系呢,下面简要总结了它们的原理、联系以及区别,可能会有理解错误的地方,望大家指出~
目录
1.网络OSI七层模型
在深入理解Socket和WebSocket之前,我们需要先掌握计算机网络的基础架构——OSI七层参考模型。这个模型就像网络通信的"建筑蓝图",将复杂的网络通信过程分解为七个逻辑层次,每一层都有其独特的功能和责任。

| 层级 | 功能 |
| 应用层 | 直接为应用程序提供网络服务 |
| 表示层 | 数据格式转换和加密 |
| 会话层 | 建立、管理和终止会话 |
| 传输层 | 提供端到端的可靠数据传输 |
| 网络层 | 实现跨网络的寻址和路由 |
| 数据链路层 | 将比特流组织成"帧",在直接相连的节点间可靠传输 |
| 物理层 | 传输原始比特流(0和1) |
为什么网络通信需要OSI模型,加个类比:
想象你要给国外的朋友寄一封信:
你需要用双方都懂的语言(应用层)
按照一定格式书写(表示层)
建立通信会话(会话层)
选择快递公司(传输层)
填写地址(网络层)
打包贴标签(数据链路层)
实际运输(物理层)
OSI模型正是这样将网络通信的复杂过程模块化,让不同厂商的设备可以相互协作。
以上简要介绍了网络通信的OSI模型,而本次介绍的socket是主要工作在传输层的接口,提供主机之间的端到端通信(TCP/UDP),而websocket是主要工作在应用层,基于HTTP升级的应用协议。
socket 和 webSocket 虽然名称相似,但它们的定位、设计目标和应用场景完全不同。名称相似主要是因为 webSocket 在设计时借用了 socket 的概念,但它们在技术实现和用途上有本质区别。
下面对socket和websocket进行一些简要的总结。
2.socket
2.1socket基础概念
socket(套接字)是 操作系统提供的网络通信接口,它抽象了底层网络协议(如TCP/IP),允许应用程序通过简单的API进行网络数据传输,本质是传输层的代理。

实际上socket就是一套基于TCP/IP通信的接口,我们在应用层上调用socket库就可以直接控制 TCP/UDP 报头的生成(如端口号、校验和),数据封装如下:
[应用数据] → socket → [TCP/UDP头][数据] → 网络层
2.2socket编程
1、socket()——创建套接字
/*
*domain:协议族, AF_INET(IPv4); AF_INET6(IPv6)
*type:通信类型, SOCK_STREAM(TCP);SOCK_DGRAM(UDP)
*protocol:通常设为 0(自动选择)
*/
int socket(int domain, int type, int protocol);
2、bind()——绑定地址
/*
*将套接字绑定到特定 IP 和端口
*sockfd:套接字描述符
*addr:指向 sockaddr_in 的指针
*addrlen:地址结构体大小
*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
示例:
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有接口
serv_addr.sin_port = htons(8080); // 端口号
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
3、listen()——监听
/*
*将套接字设置为监听模式
*sockfd:套接字描述符
*backlog:等待连接队列的最大长度
*/
int listen(int sockfd, int backlog);
示例:
if (listen(sockfd, 5) == -1) { // 允许5个连接排队
perror("listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
4、accept()——接受连接
/*
*sockfd:套接字描述符
*addr:客户端地址信息
*addrlen:地址信息长度
*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
示例:
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int new_sock = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
if (new_sock == -1) {
perror("accept failed");
}
5、connect()——客户端连接
/*
*sockfd:套接字描述符
*addr:服务器地址信息
*addrlen:地址信息长度
*/
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
示例:
struct sockaddr_in serv_addr = {
.sin_family = AF_INET,
.sin_port = htons(8080),
.sin_addr.s_addr = inet_addr("192.168.1.100")
};
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
perror("connect failed");
}
6.send()/recv()——数据收发
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
7.close()——关闭连接
int close(int fd);
3.websocket
3.1websocket基础概念
WebSocket 是一种支持全双工通信的网络协议,专为实时数据传输设计。它是建立在TCP协议之上,提供可靠的双向通信。它属于应用层上的协议,在HTTP基础之上升级的协议。它的优势是只需要较少的开销,在客户端、服务端数据交互时,协议控制的数据包头部较小,并且在建立好连接后,无需重复握手,效率更高。
3.2websocket建立连接的过程
websocket连接的过程复用了HTTP的连接过程,在建立完连接后,按照websocket的协议来进行数据交互。
(1)握手阶段(HTTP Upgrade)
-
客户端发起一个 HTTP 请求,头部包含:
GET /chat HTTP/1.1 Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13
Connection: Upgrade —— 需要升级协议;
Upgrade: websocket —— 需要升级到websocket协议;
Sec-Websocket-Version: 13 —— 表示websocket的版本,如果服务端不支持这个版本,则需要返回一个Sec-Websocket-Version Header,里面包含服务端所支持的版本号;
Sec-Websocket-Key: dGhlIHNhbXBsZSBub25jZQ== —— 与后面服务端响应首部的Sec-Websocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。
-
服务器返回
101 Switching Protocols响应:HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
3.3websocket编程
3.3.1服务端
1.服务端初始化 WebSocket 上下文
/*
*作用:初始化websocket上下文
*info:配置信息(端口、协议、回调等)
*/
struct lws_context *lws_create_context(struct lws_context_creation_info *info);
struct lws_context_creation_info info = {
.port = 8000, // 监听端口
.protocols = protocols, // 协议列表
.gid = -1, // 组ID(-1表示默认)
.uid = -1, // 用户ID(-1表示默认)
};
2.服务端协议回调函数
/*
*作用:处理 WebSocket 事件(连接、接收数据、关闭等)
*/
int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
switch(reason) {
case LWS_CALLBACK_ESTABLISHED:
// 处理新连接
break;
case LWS_CALLBACK_RECEIVE:
// 处理接收数据: in 为数据指针,len 为长度
break;
}
return 0;
}
关键事件:
LWS_CALLBACK_ESTABLISHED:客户端连接成功。
LWS_CALLBACK_RECEIVE:收到客户端数据。
LWS_CALLBACK_CLOSED:客户端断开连接。
3.服务端发送数据
char buf[LWS_PRE + 128];
memcpy(buf + LWS_PRE, "Hello Client", 12);
/*
*wsi: websocket 连接句柄
*buf: 数据缓冲区 需预留 LWS_PRE 字节头部空间(通常 4 字节),实际应从buf + LWS_PRE开始写入
*len: 仅计算实际数据部分(不包括 LWS_PRE)
*protocol: 数据帧类型标志
• 可选值:
- LWS_WRITE_TEXT:文本数据(UTF-8编码)
- LWS_WRITE_BINARY:二进制数据
- LWS_WRITE_PING:Ping 帧(保活)
- LWS_WRITE_PONG:Pong 帧(响应 Ping)
- LWS_WRITE_CLOSE:关闭连接帧
*/
int lws_write(
struct lws *wsi, // WebSocket 连接句柄
unsigned char *buf, // 数据缓冲区
size_t len, // 数据长度
enum lws_write_protocol protocol // 数据类型标志
);
lws_write(wsi, buf + LWS_PRE, 12, LWS_WRITE_TEXT);
注意:lws_write() 必须在 lws_service() 所在的线程调用(非线程安全)
3.3.2客户端
1.客户端连接初始化
/*
*info: 客户端连接配置结构体指针
*/
struct lws *lws_client_connect_via_info(const struct lws_client_connect_info *info);
结构体配置
struct lws_client_connect_info {
struct lws_context *context; // 必填:WebSocket 上下文
const char *address; // 必填:服务器地址(如 "192.168.1.100")
int port; // 必填:服务器端口(如 8000)
const char *path; // 必填:连接路径(如 "/ws")
const char *host; // 必填:Host 头(通常同 address)
const char *origin; // 必填:Origin 头(通常同 address)
const char *protocol; // 选填:子协议名称(如 "chat-protocol")
void *userdata; // 选填:用户自定义数据
};
2.客户端事件回调
/*
*wsi: 当前 webSocket 连接的句柄
*reason: 触发回调的具体事件类型
连接生命周期
LWS_CALLBACK_ESTABLISHED 连接成功建立(服务端) 初始化会话数据
LWS_CALLBACK_CLIENT_ESTABLISHED 客户端连接成功 发送首次消息
LWS_CALLBACK_CLOSED 连接关闭 释放资源
数据交互
LWS_CALLBACK_RECEIVE 收到数据 处理消息内容
LWS_CALLBACK_CLIENT_RECEIVE 客户端收到数据 解析服务端响应
LWS_CALLBACK_SERVER_WRITEABLE 服务端可发送数据 主动推送数据
LWS_CALLBACK_CLIENT_WRITEABLE 客户端可发送数据 发送请求
控制帧
LWS_CALLBACK_WSI_CREATE 连接对象创建 分配资源
LWS_CALLBACK_WSI_DESTROY 连接对象销毁 清理资源
*user: 指向用户自定义数据的指针
*in: 事件相关的数据指针,内容因 reason 而异
*len: in数据的有效长度
*/
int callback_websocket(
struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len
);
示例:
int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason,
void *user, void *in, size_t len) {
switch (reason) {
// 连接建立
case LWS_CALLBACK_ESTABLISHED:
printf("Client connected\n");
break;
// 收到数据(服务端)
case LWS_CALLBACK_RECEIVE: {
printf("Raw data (len=%zu): ", len);
for (size_t i = 0; i < len; i++)
putchar(((char *)in)[i]);
printf("\n");
unsigned char buf[LWS_PRE + 256];
memcpy(buf + LWS_PRE, in, len);
lws_write(wsi, buf + LWS_PRE, len, LWS_WRITE_TEXT);
break;
}
// 客户端可发送数据
case LWS_CALLBACK_CLIENT_WRITEABLE: {
unsigned char msg[LWS_PRE + 128];
const char *text = "Hello from Client";
memcpy(msg + LWS_PRE, text, strlen(text));
lws_write(wsi, msg + LWS_PRE, strlen(text), LWS_WRITE_TEXT);
break;
}
// 连接关闭
case LWS_CALLBACK_CLOSED:
printf("Connection closed\n");
break;
default:
break;
}
return 0;
}
3.客户端发送数据
也是使用int lws_write(struct lws *wsi, unsigned char *buf, size_t len, enum lws_write_protocol protocol); 参考服务端。
4.关闭连接
/*
*status: 关闭状态码(如 LWS_CLOSE_STATUS_NORMAL)
*reason: 可选关闭原因描述
*/
int lws_close_reason(
struct lws *wsi,
enum lws_close_status status,
unsigned char *reason,
size_t len
);
注意:调用后会触发LWS_CALLBACK_CLOSED释放资源。
4.socket和websocket的联系与区别
| 特性 | Socket | webSocket |
|---|---|---|
| 本质 | 操作系统提供的通信接口 | 基于HTTP升级的应用层协议 |
| 层级归属 | 传输层服务接口 | 应用层协议实现 |
| 编程视角 | 系统调用(syscall) | 应用协议(类似HTTP) |
| 数据控制 | 直接操作TCP/UDP报文 | 使用定义好的消息帧格式 |
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)