Socket(套接字)

Socket(套接字)是网络通信的核心接口,本质是操作系统提供的、用于进程间网络通信的"文件描述符 "(类Unix)或"句柄"(Windows)。根据通信类型,Socket主要分为流式Socket(TCP)数据报Socket(UDP)原始Socket,其中TCP/UDP是最常用的两类。

下面分核心函数参数详解返回值与功能使用流程四部分,详细介绍Socket的关键接口(以C语言为例,跨语言逻辑一致)。

一、Socket核心函数总览

网络通信的核心流程分为创建Socket、绑定地址、监听/连接、收发数据、关闭Socket,对应核心函数如下:

  1. socket() :创建Socket文件描述符
  2. bind() :绑定IP地址和端口号
  3. listen() :TCP服务端监听连接(仅TCP)
  4. accept() :TCP服务端接受客户端连接(仅TCP)
  5. connect() :TCP客户端连接服务端(仅TCP)
  6. send() / sendto() :发送数据
  7. recv() / recvfrom() :接收数据
  8. 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_STREAMIPPROTO_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 等待连接的队列长度(内核维护的半连接+全连接队列总和) 限制同时等待连接的客户端数量,通常填 510(现代系统可更大)

返回值与功能

  • 成功 :返回 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为小端序,需用转换函数保证数据格式一致:

  1. htons() :主机字节序 → 网络字节序(端口号, short 类型);
  2. htonl() :主机字节序 → 网络字节序(IP地址, int 类型);
  3. ntohs() :网络字节序 → 主机字节序(端口号);
  4. 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()
相关推荐
孙同学_5 小时前
【Linux篇】Socket编程TCP
linux·网络·tcp/ip
虾..6 小时前
Linux 基于TCP实现服务端客户端通信(线程池)
java·网络协议·tcp/ip
Xzq2105098 小时前
TCP协议
服务器·网络·tcp/ip
小杰帅气8 小时前
Tcp理解传输层
服务器·网络·tcp/ip
IpdataCloud9 小时前
智能家居设备上线IP归属分析:从设备发现到区域功能适配
网络协议·tcp/ip·智能家居
zephyr059 小时前
TCP/IP协议族详解:应用层协议HTTP
网络协议·tcp/ip·http
数据知道9 小时前
MongoDB网络安全配置:详细讲述IP白名单与防火墙规则
tcp/ip·mongodb·web安全
ipooipoo11889 小时前
kookeey 动态住宅 IP 使用场景详解:跨境电商、爬虫、社媒养号一站式解决方案
爬虫·网络协议·tcp/ip
橘子1310 小时前
网络层IP协议
网络·tcp/ip·智能路由器