news 2026/4/27 14:52:44

Socket 编程接口详细介绍

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Socket 编程接口详细介绍

Socket(套接字)是操作系统提供的标准网络编程 API,封装 TCP/IP 协议栈,实现跨主机 / 跨进程通信,遵循 “一切皆文件” 思想,以文件描述符(sockfd)操作。下面从核心概念、完整 API、TCP/UDP 流程、错误处理、示例全维度详解。

一、Socket 核心基础

1.1 核心三要素(唯一标识通信端点)

  • 地址族(domain/AF_xxx):指定网络类型
    • AF_INET:IPv4(最常用)
    • AF_INET6:IPv6
    • AF_UNIX/AF_LOCAL:本地进程通信(Unix 域)
  • 套接字类型(type/SOCK_xxx):指定传输语义
    • SOCK_STREAMTCP,面向连接、可靠、字节流、有序无重复
    • SOCK_DGRAMUDP,无连接、不可靠、数据报、无序
    • SOCK_RAW:原始套接字,直接操作 IP/ICMP 等网络层
  • 协议(protocol):通常填0(自动匹配默认:TCP 对应 IPPROTO_TCP,UDP 对应 IPPROTO_UDP)

1.2 地址结构体(通用 / IPv4)

// 通用地址(所有socket函数参数要求) struct sockaddr { sa_family_t sa_family; // 地址族 AF_xxx char sa_data[14]; // 地址数据(IP+端口) }; // IPv4专用(实际使用,强制转换为struct sockaddr*) struct sockaddr_in { sa_family_t sin_family; // AF_INET in_port_t sin_port; // 端口号(必须网络字节序,htons()) struct in_addr sin_addr;// IP地址(htonl()) char sin_zero[8]; // 填充,全0 }; // 常用宏:INADDR_ANY(绑定所有网卡IP,0.0.0.0)

1.3 字节序转换(必须)

主机字节序(小端)↔网络字节序(大端):

  • htons():主机短整型→网络短整型(端口)
  • htonl():主机长整型→网络长整型(IP)
  • ntohs()ntohl():反向转换

二、核心 Socket API(函数原型 + 参数 + 返回值 + 用法)

下面介绍程序中用到的socket API,这些函数都在sys/socket.h中。

#include <sys/types.h> #include <sys/socket.h>

2.1 socket () —— 创建套接字(入口)

int socket(int domain, int type, int protocol);
  • 参数:对于IPv4, family参数指定为AF_INET;
    对于TCP协议,type参数指定为SOCK_STREAM, 表示面向流的传输协议;对于UDP协议,type参数指定为SOCK_DGRAM,表示面向数据报的传输协议; protocol参数的介绍从略,指定为0即可。
  • 返回:成功→非负整数sockfd(文件描述符);失败→-1,设置errno
  • 作用:socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符;
    应用程序可以像读写文件一样用read/write在网络上收发数据;

2.2 bind () —— 绑定 IP + 端口(服务端必须)

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端
口号后就可以向服务器发起连接; 服务器需要调用bind绑定一个固定的网络地址和端口号;

  • 参数:sockfd(socket 返回值)、addr(sockaddr_in,填 IP + 端口)、addrlen(sizeof (struct sockaddr_in))
  • 返回:成功→0;失败→-1(常见错误:端口被占用 EADDRINUSE、权限不足 EACCES)
  • 作用:bind()的作用是将参数sockfd和myaddr绑定在一起, 使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号;

struct sockaddr *是一个通用指针类型,myaddr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需要第三个参数addrlen指定结构体的长度;

我们的程序中对myaddr参数是这样初始化的:

1. 将整个结构体清零;
2. 设置地址类型为AF_INET;
3. 网络地址为INADDR_ANY, 这个宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网也可能绑定多个IP 地址, 这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP 地址;
4. 端口号为SERV_PORT, 我们定义为9999;

2.3 listen () —— TCP 服务端监听连接(仅 TCP)

int listen(int sockfd, int backlog);
  • 参数:sockfd(已 bind 的监听 fd)、backlog(半连接 + 全连接队列总长度,受内核net.core.somaxconn限制,默认 128)
  • 返回:成功→0;失败→-1
  • 作用:将 socket 设为被动监听模式,等待客户端连接,内核维护连接队列

2.4 accept () —— TCP 服务端接受连接(仅 TCP)

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • 参数:sockfd(监听 fd);addr是一个传出参数,accept()返回时传出客户端的地址和端口号;如果给addr 传 NULL,表示不关心客户端的地址; addrlen参数是一个传入传出参数(value-result argument), 传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题, 传出的是客户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区);
  • 返回:成功→新的客户端 fd(专用于和该客户端通信);失败→-1(阻塞时无连接挂起,非阻塞返回 EAGAIN)
  • 作用:从全连接队列取一个连接,创建新通信套接字,三次握手完成后, 服务器调用accept()接受连接;如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来;

2.5 connect () —— TCP 客户端发起连接(仅 TCP)

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数:sockfd(客户端 socket)、addr(服务端 IP + 端口)、addrlen(地址长度)
  • 返回:成功→0;失败→-1(ECONNREFUSED:端口无监听、ETIMEDOUT:超时)
  • 作用:触发 TCP三次握手,建立可靠连接,客户端需要调用connect()连接服务器;

connect和bind的参数形式一致, 区别在于bind的参数是自己的地址, 而connect的参数是对方的
地址;

2.6 send () /write () —— TCP 发送数据(已连接)

ssize_t send(int sockfd, const void *buf, size_t len, int flags); ssize_t write(int sockfd, const void *buf, size_t len); // 等价send(flags=0)
  • 参数:sockfd(已连接 fd)、buf(数据缓冲区)、len(发送长度)、flags(0 默认;MSG_OOB 带外数据;MSG_DONTWAIT 非阻塞)
  • 返回:成功→实际发送字节数(可能 < len,需循环发送);失败→-1
  • 注意:TCP 是流,无边界,需自定义分包(先发长度再发数据)

2.7 recv () /read () —— TCP 接收数据(已连接)

ssize_t recv(int sockfd, void *buf, size_t len, int flags); ssize_t read(int sockfd, void *buf, size_t len); // 等价recv(flags=0)
  • 参数:sockfd、buf(接收缓冲区)、len(最大接收长度)、flags(0 默认;MSG_PEEK 偷看不清除;MSG_DONTWAIT 非阻塞)
  • 返回:成功→接收字节数;0→对端关闭连接;失败→-1

2.8 sendto () —— UDP 发送(无连接,指定目标)

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
  • 参数:比 send 多目标地址 dest_addr(服务端 IP + 端口)
  • 返回:成功→发送字节数;失败→-1
  • 特点:无需 connect,每次指定目标,数据报独立

2.9 recvfrom () —— UDP 接收(获取源地址)

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
  • 参数:比 recv 多 src_addr(输出发送方 IP + 端口)、addrlen(长度)
  • 返回:成功→接收字节数;失败→-1
  • 特点:UDP 无连接,必须用此函数获取发送方地址

2.10 close () /closesocket () —— 关闭套接字

int close(int sockfd); // Linux/Unix int closesocket(SOCKET sockfd); // Windows(Winsock)
  • 作用:释放文件描述符,关闭连接;TCP 会触发四次挥手;UDP 直接释放资源
  • 注意:必须关闭,避免资源泄漏

2.11 setsockopt () /getsockopt () —— 设置 / 获取套接字选项

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
  • 常用选项
    • SO_REUSEADDR:允许端口重用(解决 TIME_WAIT 问题)
    • SO_RCVBUF/SO_SNDBUF:设置接收 / 发送缓冲区大小
    • SO_KEEPALIVE:开启 TCP 保活
    • TCP_NODELAY:禁用 Nagle 算法(降低延迟)

三、TCP vs UDP 完整调用流程

3.1 TCP 服务端流程(面向连接,可靠)

  1. socket(AF_INET, SOCK_STREAM, 0)→ 创建监听 fd
  2. bind()→ 绑定 IP: 端口
  3. listen(backlog)→ 开启监听
  4. accept()→ 阻塞等待,获取客户端 fd
  5. send()/recv()→ 与客户端收发数据
  6. close(客户端fd)→ 关闭单个连接
  7. close(监听fd)→ 停止服务

3.2 TCP 客户端流程

  1. socket(AF_INET, SOCK_STREAM, 0)→ 创建客户端 fd
  2. connect(服务端IP:端口)→ 发起三次握手
  3. send()/recv()→ 收发数据
  4. close()→ 关闭连接

3.3 UDP 流程(无连接,不可靠,简化)

  • 服务端:socket→bind→recvfrom(循环接收,获取源地址)→sendto(回复)→close
  • 客户端:socket→sendto(指定服务端地址)→recvfrom(接收)→close
  • 特点:无 listen/accept/connect,直接收发数据报

四、错误处理与常见 errno

所有 Socket 函数失败均返回 - 1,需用perror()strerror(errno)打印错误:

  • EADDRINUSE:端口已被占用
  • ECONNREFUSED:连接被拒绝(服务端未监听)
  • ETIMEDOUT:连接 / 接收超时
  • EAGAIN/EWOULDBLOCK:非阻塞模式下无数据 / 无连接
  • EPIPE:向已关闭的连接发送(需忽略 SIGPIPE 信号)

五、补充参考内容

地址转换函数

本节只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP
地址
但是我们通常用点分十进制的字符串表示IP 地址,以下函数可以在字符串表示 和in_addr表示之间转换;
字符串转in_addr的函数:

in_addr转字符串的函数:

其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是
void *addrptr。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/27 14:52:35

Python高级编程11:PyMySQL

目录 前言 一、上节快速回顾 二、可视化管理工具 Navicat 如何安装Navicat 如何运行 SQL 文件&#xff1f; 三、视图&#xff08;View&#xff09; 3.1 什么是视图&#xff1f; 3.2 基本语法 3.3 使用示例 四、索引&#xff08;Index&#xff09; 4.1 为什么需要索引…

作者头像 李华
网站建设 2026/4/27 14:52:30

YOLO26红外无人机识别检测系统(项目源码+YOLO数据集+模型权重+UI界面+python+深度学习+远程环境部署)

摘要 针对红外场景下无人机目标检测任务&#xff0c;本文基于YOLO26框架构建了一套高精度识别系统。实验采用包含5019张训练图像与1233张验证图像的红外无人机数据集&#xff0c;类别为单一目标drone。模型训练后&#xff0c;在验证集上取得了mAP50为0.981、Precision为0.986、…

作者头像 李华
网站建设 2026/4/27 14:46:53

猫抓插件:终极浏览器资源嗅探指南,轻松下载网页视频和音频

猫抓插件&#xff1a;终极浏览器资源嗅探指南&#xff0c;轻松下载网页视频和音频 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 还在为无法保存网…

作者头像 李华
网站建设 2026/4/27 14:46:25

别再只懂.mp4后缀了!手把手带你用Hex编辑器‘解剖’MP4文件,看懂ftyp、moov、mdat这些Box到底存了什么

用十六进制编辑器拆解MP4文件&#xff1a;从二进制字节到视频播放的奥秘 当你在手机或电脑上双击一个MP4文件时&#xff0c;播放器几乎瞬间就能开始播放视频。但你是否想过&#xff0c;这个看似简单的文件内部究竟藏着怎样的秘密&#xff1f;本文将带你用十六进制编辑器直接&qu…

作者头像 李华