前言对于C/C初学者来说网络编程似乎是一道门槛而Socket就是打开这扇门的钥匙。今天我们一起来看看如何入门Socket网络编程。目录一、什么是Socket二、Socket编程流程三、TCP Socket编程示例四、一些注意事项一、什么是SocketSocket套接字是操作系统提供的网络通信编程接口是应用层与传输层之间的桥梁。┌─────────────────────────────────────┐ │ 应用层 (HTTP/FTP/DNS) ├─────────────────────────────────────┤ │ Socket 接口 ← 我们编程的位置 ├─────────────────────────────────────┤ │ 传输层 (TCP/UDP) ├─────────────────────────────────────┤ │ 网络层 (IP/ICMP) ├─────────────────────────────────────┤ │ 网络接口层 (以太网/WiFi) └─────────────────────────────────────┘1) Socket的作用通信信道Socket 为网络中的数据传输提供了一个虚拟信道数据可以通过这个信道从发送方传输到接收方地址识别通过 IP 地址和端口号Socket 能够标识网络中的特定设备和应用程序协议兼容性Socket 允许应用使用不同的传输层协议来进行通信如 TCP 和 UDP。2) Socket的类型类型协议特点应用场景流式套接字TCP面向连接、可靠、有序、无边界文件传输、网页浏览、数据库数据报套接字UDP无连接、不可靠、有边界、高效视频直播、DNS、在线游戏原始套接字IP/ICMP直接访问底层协议网络诊断、安全工具二、Socket编程流程1) 服务端编程步骤创建套接字socket使用socket()函数创建一个新的套接字。绑定套接字bind通过bind()函数将套接字与特定的IP地址和端口号关联起来。监听连接listen使用listen()函数使服务器套接字监听来自客户端的连接请求。接受连接accept当客户端请求连接时accept()函数会接受这个连接。读取数据read/recv从客户端接收数据。发送数据write/send向客户端发送数据。关闭套接字close完成数据传输后关闭连接。2) 客户端编程步骤创建套接字socket同服务端。发起连接connect使用connect()函数向服务器发起连接请求。发送数据write/send向服务器发送数据。读取数据read/recv从服务器接收数据。关闭套接字close完成数据传输后关闭连接。例如TCP Socket通信流程服务端Server 客户端Client │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ socket() │ 创建套接字 │ socket() │ │ 创建监听fd │ │ 创建通信fd | └──────┬──────┘ └──────┬──────┘ │ │ ▼ │ ┌─────────────┐ │ │ bind() │ 绑定IP:端口 │ │ 0.0.0.0:8080│ │ └──────┬──────┘ │ │ │ ▼ │ ┌─────────────┐ │ │ listen() │ 开始监听backlog队列 │ │ 被动等待连接│ │ └──────┬──────┘ │ │◄────────────────────────────────────────┤ │ connect() 发起连接 │ │ 三次握手开始... │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ accept() │◄───────────────────────► │ 连接建立 │ │ 阻塞等待连接 │ 返回新的通信fd(connfd) │ │ └──────┬──────┘ └──────┬──────┘ │ │ │◄────────── 双向数据传输 ───────────────► │ │ read()/write() 或 recv()/send() │ │ │ │ 四次挥手断开连接... │ │◄───────────────────────────────────────►│ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ close() │ │ close() │ │ 关闭connfd │ │ 关闭fd │ │ 关闭listenfd│ │ │ └─────────────┘ └─────────────┘三、TCP Socket编程示例本文仅演示基础版本(阻塞式TCP通信单线程)服务器编程// server_basic.cpp #include iostream #include cstring #include string #include unistd.h // close, read, write #include arpa/inet.h // inet_addr, htons #include sys/socket.h // socket, bind, listen, accept int main() { // 1. 创建套接字 (IPv4, TCP, 默认协议) int server_fd socket(AF_INET, SOCK_STREAM, 0); if (server_fd -1) { std::cerr 创建socket失败 std::endl; return -1; } std::cout [1] Socket创建成功, fd server_fd std::endl; // 2. 绑定地址和端口 struct sockaddr_in server_addr; memset(server_addr, 0, sizeof(server_addr)); // 清零 server_addr.sin_family AF_INET; // IPv4 server_addr.sin_port htons(8080); // 端口8080 (转网络字节序) server_addr.sin_addr.s_addr INADDR_ANY; // 监听所有网卡 0.0.0.0 if (bind(server_fd, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) { std::cerr 绑定失败 std::endl; close(server_fd); return -1; } std::cout [2] 绑定成功 0.0.0.0:8080 std::endl; // 3. 开始监听 (最大等待队列长度5) if (listen(server_fd, 5) -1) { std::cerr 监听失败 std::endl; close(server_fd); return -1; } std::cout [3] 开始监听... std::endl; // 4. 接受客户端连接 (阻塞等待) struct sockaddr_in client_addr; socklen_t addr_len sizeof(client_addr); std::cout [4] 等待客户端连接... std::endl; int client_fd accept(server_fd, (struct sockaddr*)client_addr, addr_len); if (client_fd -1) { std::cerr 接受连接失败 std::endl; close(server_fd); return -1; } char client_ip[16]; inet_ntop(AF_INET, client_addr.sin_addr, client_ip, sizeof(client_ip)); std::cout [5] 客户端连接! IP client_ip 端口 ntohs(client_addr.sin_port) std::endl; // 5. 数据收发循环 char buffer[1024]; while (true) { // 接收数据 memset(buffer, 0, sizeof(buffer)); int recv_len recv(client_fd, buffer, sizeof(buffer) - 1, 0); if (recv_len 0) { std::cout [6] 客户端断开连接 std::endl; break; } std::cout [收到] buffer std::endl; // 发送响应 std::string response 服务器收到: ; response buffer; send(client_fd, response.c_str(), response.length(), 0); } // 6. 关闭连接 close(client_fd); close(server_fd); std::cout [7] 服务器关闭 std::endl; return 0; }客户端编程// client_basic.cpp #include iostream #include cstring #include string #include unistd.h #include arpa/inet.h #include sys/socket.h int main() { // 1. 创建套接字 int sock_fd socket(AF_INET, SOCK_STREAM, 0); if (sock_fd -1) { std::cerr 创建socket失败 std::endl; return -1; } std::cout [1] Socket创建成功 std::endl; // 2. 设置服务器地址 struct sockaddr_in server_addr; memset(server_addr, 0, sizeof(server_addr)); server_addr.sin_family AF_INET; server_addr.sin_port htons(8080); // 服务器端口 // 将IP字符串转为网络字节序 if (inet_pton(AF_INET, 127.0.0.1, server_addr.sin_addr) 0) { std::cerr 无效的IP地址 std::endl; close(sock_fd); return -1; } // 3. 连接服务器 std::cout [2] 正在连接服务器... std::endl; if (connect(sock_fd, (struct sockaddr*)server_addr, sizeof(server_addr)) -1) { std::cerr 连接失败 std::endl; close(sock_fd); return -1; } std::cout [3] 连接成功! std::endl; // 4. 发送和接收数据 char buffer[1024]; while (true) { // 输入消息 std::cout 请输入消息 (输入quit退出): ; std::string msg; std::getline(std::cin, msg); if (msg quit) { break; } // 发送 if (send(sock_fd, msg.c_str(), msg.length(), 0) -1) { std::cerr 发送失败 std::endl; break; } // 接收响应 memset(buffer, 0, sizeof(buffer)); int recv_len recv(sock_fd, buffer, sizeof(buffer) - 1, 0); if (recv_len 0) { std::cout 服务器断开连接 std::endl; break; } std::cout [服务器回复] buffer std::endl; } // 5. 关闭 close(sock_fd); std::cout [4] 客户端关闭 std::endl; return 0; }四、一些注意事项1)网络字节序问题端口、IP 地址在协议头中都是网络字节序大端如果直接填主机字节序在大小端不同的机器上会错乱。2)返回值与错误码每个系统调用都需要检查返回值并打印错误信息。3)必须关闭Socketsocket 是文件描述符或句柄不关闭会慢慢耗尽资源。