在网络通信中,我们可能会常常用到socket编程、websocket、HTTP通信、TCP/IP等,那它们是如何通信,有什么联系呢,下面简要总结了它们的原理、联系以及区别,可能会有理解错误的地方,望大家指出~


目录

1.网络OSI七层模型

2.socket

2.1socket基础概念

2.2socket编程

1、socket()——创建套接字

2、bind()——绑定地址

3、listen()——监听

4、accept()——接受连接

5、connect()——客户端连接

 6.send()/recv()——数据收发

 7.close()——关闭连接

3.websocket

3.1websocket基础概念

3.2websocket建立连接的过程

(1)握手阶段(HTTP Upgrade)

3.3websocket编程

3.3.1服务端

3.3.2客户端

4.socket和websocket的联系与区别


1.网络OSI七层模型

       在深入理解Socket和WebSocket之前,我们需要先掌握计算机网络的基础架构——OSI七层参考模型。这个模型就像网络通信的"建筑蓝图",将复杂的网络通信过程分解为七个逻辑层次,每一层都有其独特的功能和责任。

层级 功能
应用层 直接为应用程序提供网络服务
表示层 数据格式转换和加密
会话层 建立、管理和终止会话
传输层 提供端到端的可靠数据传输
网络层 实现跨网络的寻址和路由
数据链路层 将比特流组织成"帧",在直接相连的节点间可靠传输
物理层 传输原始比特流(0和1)

 为什么网络通信需要OSI模型,加个类比:

想象你要给国外的朋友寄一封信:

  1. 你需要用双方都懂的语言(应用层)

  2. 按照一定格式书写(表示层)

  3. 建立通信会话(会话层)

  4. 选择快递公司(传输层)

  5. 填写地址(网络层)

  6. 打包贴标签(数据链路层)

  7. 实际运输(物理层)

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报文 使用定义好的消息帧格式

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐