Socket(套接字)是网络通信的核心接口,本质是操作系统提供的、用于进程间网络通信的"文件描述符 "(类Unix)或"句柄"(Windows)。根据通信类型,Socket主要分为流式Socket(TCP) 、数据报Socket(UDP)和原始Socket,其中TCP/UDP是最常用的两类。
下面分核心函数 、参数详解 、返回值与功能 、使用流程四部分,详细介绍Socket的关键接口(以C语言为例,跨语言逻辑一致)。
一、Socket核心函数总览
网络通信的核心流程分为创建Socket、绑定地址、监听/连接、收发数据、关闭Socket,对应核心函数如下:
- socket() :创建Socket文件描述符
- bind() :绑定IP地址和端口号
- listen() :TCP服务端监听连接(仅TCP)
- accept() :TCP服务端接受客户端连接(仅TCP)
- connect() :TCP客户端连接服务端(仅TCP)
- send() / sendto() :发送数据
- recv() / recvfrom() :接收数据
- close() / closesocket() :关闭Socket
二、核心函数参数、返回值与功能详解
1. socket():创建Socket
函数原型
c
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
参数说明
| 参数 | 取值/含义 | 核心作用 |
|---|---|---|
| 协议族(domain) | • AF_INET:IPv4协议• AF_INET6:IPv6协议• AF_UNIX:本地进程通信 |
指定Socket的通信域,决定地址格式(如IPv4用 struct sockaddr_in) |
| Socket类型(type) | • SOCK_STREAM:流式Socket(TCP,面向连接、可靠)• SOCK_DGRAM:数据报Socket(UDP,无连接、不可靠)• SOCK_RAW:原始Socket(需管理员权限) |
决定数据传输方式:TCP用流传输,UDP用数据报传输 |
| 协议类型(protocol) | 通常填 0(自动匹配):• IPPROTO_TCP:TCP协议• IPPROTO_UDP:UDP协议 |
指定具体传输协议,填 0 时内核自动匹配(如 AF_INET + SOCK_STREAM → IPPROTO_TCP) |
返回值与功能
- 成功 :返回非负整数(Socket文件描述符,后续所有操作都用这个值);
- 失败:返回 -1 ,并设置全局变量 errno (错误码,可通过 perror() 打印错误信息)。
- 功能:创建一个用于网络通信的Socket实例,分配内核资源(缓冲区、协议控制块等)。
2. bind():绑定地址与端口
函数原型
c
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数说明
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | socket() 返回的文件描述符 | 指定要绑定的Socket |
| addr | 地址结构体指针,需强制转换:• IPv4用 struct sockaddr_in *• 通用类型为 struct sockaddr * |
存储要绑定的IP地址和端口号 |
| addrlen | 地址结构体的长度(如 sizeof(struct sockaddr_in)) |
告诉内核地址结构体的大小,避免越界 |
关键地址结构体(以IPv4为例):
c
struct sockaddr_in {
sa_family_t sin_family; // 协议族(AF_INET)
in_port_t sin_port; // 端口号(网络字节序,用htons()转换)
struct in_addr sin_addr; // IP地址
};
struct in_addr {
uint32_t s_addr; // IP地址(网络字节序,用inet_addr()转换)
};
- 端口号范围: 0~65535 , 0~1023 为知名端口(需管理员权限),建议用 1024~49151 的动态端口;
- IP地址: INADDR_ANY (表示绑定本机所有网卡IP,服务端常用),或具体IP(如 192.168.1.100 )。
返回值与功能
- 成功 :返回 0 ;
- 失败 :返回 -1 ,设置 errno ;
- 功能:将Socket与指定的IP地址和端口号绑定,让其他设备能通过该地址访问当前Socket。
3. listen():TCP服务端监听连接(仅TCP)
函数原型
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | 已绑定的Socket文件描述符 | 指定要监听的Socket |
| backlog | 等待连接的队列长度(内核维护的半连接+全连接队列总和) | 限制同时等待连接的客户端数量,通常填 5 或 10(现代系统可更大) |
返回值与功能
- 成功 :返回 0 ;
- 失败 :返回 -1 ,设置 errno ;
- 功能 :将Socket转为被动监听模式,等待客户端发起TCP连接请求,进入监听状态后才能接收连接。
4. accept():TCP服务端接受连接(仅TCP)
函数原型
c
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数说明
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | 监听状态的Socket文件描述符 | 监听用的Socket |
| addr | 输出参数,存储客户端的地址信息(IP+端口),填 NULL 表示不获取 |
记录客户端的身份 |
| addrlen | 输入输出参数:• 输入:地址结构体的最大长度• 输出:实际客户端地址的长度 | 避免地址结构体越界 |
返回值与功能
返回值与功能
- 成功 :返回新的Socket文件描述符(通信Socket ,用于和当前客户端收发数据;原 sockfd 仍为监听Socket,可继续监听其他连接);
- 失败 :返回 -1 ,设置 errno ;
- 功能 :从等待连接队列中取出一个客户端连接,创建新的Socket用于双方通信(阻塞函数:无连接时会一直等待,直到有客户端连接)。
5. connect():TCP客户端连接服务端(仅TCP)
函数原型
c
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | 未连接的Socket文件描述符 | 客户端创建的Socket |
| servaddr | 服务端的地址信息(IP+端口) | 指定要连接的服务端地址 |
| addrlen | 地址结构体的长度 | 同 bind() |
返回值与功能
- 成功 :返回 0 ,此时客户端与服务端建立TCP连接;
- 失败 :返回 -1 ,设置 errno (如服务端无响应、端口未监听等);
- 功能 :客户端主动向服务端发起TCP三次握手,建立连接(阻塞函数:连接未建立时会一直等待,可设置非阻塞)。
6. send():TCP发送数据(或UDP发送,需已连接)
函数原型
c
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | 通信Socket(TCP的通信Socket / 已连接的UDP Socket) | 用于发送数据的Socket |
| buf | 要发送的数据缓冲区(如字符数组、结构体) | 存储待发送的数据 |
| len | 要发送的数据长度(字节数) | 指定发送的数据大小 |
| flags | 发送标志,常用:• 0:默认发送方式• MSG_OOB:发送带外数据(紧急数据)• MSG_DONTWAIT:非阻塞发送 |
控制发送行为 |
返回值与功能
- 成功 :返回实际发送的字节数 (可能小于 len ,需循环发送);
- 失败 :返回 -1 ,设置 errno ;
- 功能:向已连接的Socket发送数据(TCP保证数据可靠传输,UDP无连接时需用sendto())。
7. sendto():UDP发送数据(无连接专用)
函数原型
c
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
参数说明
- 前4个参数同 send() ;
- dest_addr :目标地址(UDP接收方的IP+端口);
- addrlen :目标地址结构体长度。
返回值与功能
- 成功:返回实际发送的字节数;失败:返回 -1 ;
- 功能:UDP无连接,直接指定目标地址发送数据报,无需先建立连接。
8. recv():TCP接收数据(或UDP接收,需已连接)
函数原型
c
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
c
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数说明
| 参数 | 含义 | 核心作用 |
|---|---|---|
| sockfd | 通信Socket | 用于接收数据的Socket |
| buf | 存储接收数据的缓冲区 | 存放收到的数据 |
| len | 缓冲区的最大长度(字节数) | 指定最多接收的字节数 |
| flags | 接收标志,常用: • 0:默认接收方式 • MSG_PEEK:查看数据但不取出 • MSG_DONTWAIT:非阻塞接收 |
控制接收行为 |
返回值与功能
- 成功 :返回实际接收的字节数:
-
0 :接收成功,字节数为实际数据长度;
- =0 :对方主动关闭连接(TCP四次挥手完成);
- 失败 :返回 -1 ,设置 errno ;
- 功能 :从已连接的Socket接收数据(阻塞函数:无数据时会一直等待)。
9. recvfrom():UDP接收数据(无连接专用)
函数原型
c
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
参数说明
- 前4个参数同 recv() ;
- src_addr :输出参数,存储发送方的地址信息;
- addrlen :输入输出参数,同accept()。
返回值与功能
- 成功:返回实际接收的字节数;失败:返回 -1 ;
- 功能:UDP无连接,直接从指定发送方接收数据报,可获取发送方地址。
10. close()/closesocket():关闭Socket
函数原型
c
// 类Unix系统
#include <unistd.h>
int close(int sockfd);
// Windows系统
#include <winsock2.h>
int closesocket(SOCKET sockfd);
参数说明
sockfd :要关闭的Socket文件描述符/句柄。
返回值与功能
- 成功 :返回 0 ;
- 失败 :返回 -1 (Windows返回 SOCKET_ERROR );
- 功能:关闭Socket,释放内核资源(缓冲区、文件描述符等),TCP会自动发起四次挥手关闭连接。
三、关键补充:字节序转换函数
Socket通信中,网络字节序为大端序,而x86架构CPU为小端序,需用转换函数保证数据格式一致:
- htons() :主机字节序 → 网络字节序(端口号, short 类型);
- htonl() :主机字节序 → 网络字节序(IP地址, int 类型);
- ntohs() :网络字节序 → 主机字节序(端口号);
- ntohl() :网络字节序 → 主机字节序(IP地址)。
示例:
c
struct sockaddr_in serv_addr;
serv_addr.sin_port = htons(8080); // 端口8080转网络字节序
serv_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // IP转网络字节序
四、Socket通信基础流程
1. TCP通信流程
- 服务端 :socket() → bind() → listen() → accept() → recv()/send() → close()
- 客户端:socket() → connect() → send()/recv() → close()
2. UDP通信流程
- 服务端 :socket() → bind() → recvfrom()/sendto() → close()
- 客户端:socket() → sendto()/recvfrom() → close()
下面给你一个完整可运行的 TCP 服务端/客户端示例,把你前面问过的这些 socket 函数都调用一遍:
- socket()
- bind()
- listen()
- accept()
- connect()
- send()
- recv()
- close()
用 C 语言 + Linux/类 Unix 风格 写,Windows 只需要把 close 换成 closesocket 并初始化 Winsock。
1. 服务端代码(server.c)
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define PORT 8080
#define BUF_SIZE 1024
int main() {
int server_fd, new_socket;
ssize_t valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUF_SIZE] = {0};
const char *hello = "Hello from server";
// 1. socket():创建 socket 文件描述符
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 设置 socket 选项,允许端口快速重用
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
&opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
address.sin_port = htons(PORT); // 主机序 → 网络序
// 2. bind():绑定 IP + 端口
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 3. listen():开始监听
if (listen(server_fd, 3) < 0) { // backlog = 3
perror("listen");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 4. accept():阻塞等待客户端连接
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
(socklen_t *)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("Client connected!\n");
// 5. recv():接收客户端数据
valread = recv(new_socket, buffer, BUF_SIZE, 0);
printf("Received from client: %s\n", buffer);
// 6. send():向客户端发送数据
send(new_socket, hello, strlen(hello), 0);
printf("Hello message sent to client\n");
// 7. close():关闭连接
close(new_socket);
close(server_fd);
return 0;
}
2. 客户端代码(client.c)
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUF_SIZE 1024
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char buffer[BUF_SIZE] = {0};
const char *hello = "Hello from client";
// 1. socket():创建 socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket creation error");
exit(EXIT_FAILURE);
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// 将点分十进制 IP 转成网络字节序
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
perror("invalid address/ address not supported");
exit(EXIT_FAILURE);
}
// 2. connect():连接服务端
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connection failed");
exit(EXIT_FAILURE);
}
// 3. send():向服务端发数据
send(sock, hello, strlen(hello), 0);
printf("Hello message sent to server\n");
// 4. recv():接收服务端返回
read(sock, buffer, BUF_SIZE);
printf("Received from server: %s\n", buffer);
// 5. close():关闭 socket
close(sock);
return 0;
}
3. 编译与运行
编译
bash
c
gcc server.c -o server
gcc client.c -o client
运行
先开服务端:
bash
c
./server
再开一个终端运行客户端:
bash
c
./client
输出效果
服务端:
plaintext
c
Server listening on port 8080...
Client connected!
Received from client: Hello from client
Hello message sent to client
客户端:
plaintext
c
Hello message sent to server
Received from server: Hello from server
4. 代码里调用了哪些你关心的函数?
- 服务端:
- socket()
- bind()
- listen()
- accept()
- recv()
- send()
- close()
- 客户端:
- socket()
- connect()
- send()
- recv()
- close()