第1章:Socket编程基础

各位同学好,我是老张。今天咱们聊聊Socket编程。说实话,我在鸿蒙系统上折腾网络协议栈也有几年了,踩过的坑不少,积累的经验也还行。这一章,我会把Socket最核心的东西讲清楚。

1.1 什么是Socket?

Socket,中文叫「套接字」。说白了,它就是网络通信的「门」。你想啊,两台设备要通信,总得有个入口吧?Socket就是这个入口。

我习惯这么理解:Socket = IP地址 + 端口号。IP地址找到设备,端口号找到设备上的具体应用。两者一组合,通信的双方就确定了。

核心要点:Socket是应用层与传输层之间的抽象接口。它屏蔽了底层协议的复杂性,让开发者可以像读写文件一样进行网络通信。

你可能会问:「为什么不用更高级的接口?」嗯,这个问题我当年也想过。后来发现,Socket虽然原始,但胜在灵活。你想控制超时、缓冲区、协议选项?Socket全给你。高级接口封装得再好,底层还是Socket。

1.2 鸿蒙Socket API概览

鸿蒙的Socket API,其实和POSIX标准很接近。如果你写过Linux下的Socket程序,上手会非常快。但有几个鸿蒙特有的点,我得提醒你注意。

先看一张表,把常用的API列出来:

函数 作用 鸿蒙注意事项
socket() 创建Socket 支持AF_INET、AF_INET6
bind() 绑定地址和端口 端口号建议大于1024
listen() 监听连接(TCP) backlog参数别设太大
connect() 发起连接(TCP) 超时处理要自己加
accept() 接受连接(TCP) 返回新的Socket fd
send() / recv() 发送/接收数据 注意返回值处理
sendto() / recvfrom() UDP发送/接收 需要指定目标地址
close() 关闭Socket 记得释放资源

我的经验:鸿蒙的socket()函数在创建失败时,返回的是-1,不是NULL。很多新手在这里翻车,检查错误时用成了if (sock == NULL),结果死活查不出问题。

1.3 TCP Socket通信流程

TCP是面向连接的协议。说白了,就是通信前先「握手」,建立一条可靠的通道。我做过一个物联网项目,设备端上报数据用的就是TCP,因为数据不能丢。

TCP通信的流程,我总结为「三步走」:

  1. 服务端:创建Socket → 绑定地址 → 监听 → 接受连接 → 收发数据 → 关闭
  2. 客户端:创建Socket → 连接服务端 → 收发数据 → 关闭
  3. 注意:服务端accept()返回的Socket和监听Socket不是同一个

来看一个简单的TCP服务端示例:

// TCP服务端 - 鸿蒙环境
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int server_fd, client_fd;
    struct sockaddr_in addr;
    char buffer[1024] = {0};
    
    // 1. 创建Socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        // 错误处理
        return -1;
    }
    
    // 2. 绑定地址
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(8080);
    
    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        close(server_fd);
        return -1;
    }
    
    // 3. 监听
    if (listen(server_fd, 5) < 0) {
        close(server_fd);
        return -1;
    }
    
    // 4. 接受连接
    client_fd = accept(server_fd, NULL, NULL);
    
    // 5. 接收数据
    recv(client_fd, buffer, sizeof(buffer), 0);
    
    // 6. 关闭
    close(client_fd);
    close(server_fd);
    
    return 0;
}

我曾经踩过的坑:有一次,我在服务端忘了调用bind(),直接listen()了。结果listen()返回成功,但客户端死活连不上。查了半天才发现,系统自动分配了一个随机端口,根本不是我想用的8080。记住:服务端必须bind(),客户端可以不用

1.4 UDP Socket通信流程

UDP就简单多了。它不需要连接,直接发数据就行。我做过一个视频流传输的项目,用的就是UDP。为什么?因为视频丢几帧没关系,但延迟大了可不行。

UDP的流程:

  1. 服务端:创建Socket → 绑定地址 → 收发数据 → 关闭
  2. 客户端:创建Socket → 收发数据 → 关闭
  3. 关键区别:没有listen()和accept(),直接用sendto()和recvfrom()

来看UDP服务端的代码:

// UDP服务端 - 鸿蒙环境
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int sock_fd;
    struct sockaddr_in addr, client_addr;
    char buffer[1024] = {0};
    socklen_t addr_len = sizeof(client_addr);
    
    // 1. 创建Socket
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock_fd < 0) {
        return -1;
    }
    
    // 2. 绑定地址
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(9090);
    
    bind(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
    
    // 3. 接收数据(不需要accept)
    recvfrom(sock_fd, buffer, sizeof(buffer), 0,
             (struct sockaddr*)&client_addr, &addr_len);
    
    // 4. 发送数据回客户端
    sendto(sock_fd, buffer, strlen(buffer), 0,
           (struct sockaddr*)&client_addr, addr_len);
    
    // 5. 关闭
    close(sock_fd);
    
    return 0;
}

我的建议:UDP虽然简单,但有个大问题——数据可能丢,也可能乱序。如果你做的是文件传输、消息推送这类要求可靠性的场景,老老实实用TCP。但如果是实时音视频、游戏同步,UDP是更好的选择。

1.5 TCP vs UDP:怎么选?

很多新手会纠结这个问题。我直接给你一个判断标准:

  • 选TCP:数据必须完整、顺序必须正确。比如文件下载、网页浏览、数据库操作。
  • 选UDP:实时性优先、可以容忍少量丢包。比如视频通话、在线游戏、DNS查询。

你想想看,如果视频通话用TCP,一旦丢包就重传,画面卡成PPT,谁受得了?反过来,银行转账用UDP,丢了一笔钱,那还得了?

一句话总结:TCP保可靠,UDP保速度。没有绝对的好坏,只有合不合适的场景。

好了,这一章的内容就到这里。Socket编程是网络通信的基石,后面的章节我们会深入鸿蒙特有的网络特性。下一章,咱们聊聊鸿蒙的分布式网络通信,那才是真正有意思的东西。


公众号:蓝海资料掘金营,微信deep3321