当前位置: 首页 > news >正文

[Linux] 再谈 Linux Socket 编程技术(代码示例)

再谈 Linux Socket 编程技术

文章目录

  • 再谈 Linux Socket 编程技术
        • 1. 简介
          • Socket 编程的基本概念
          • 网络通信模型(TCP/IP、UDP)
          • Socket 在 Linux 中的角色
        • 2. Socket 类型与协议
        • 3. 基本 Socket 函数
        • 4. TCP Socket 编程示例
        • 5. UDP Socket 编程示例
        • 6. 高级 Socket 编程技术
          • 多路复用(`select()`、`poll()`、`epoll()`)
          • 非阻塞 Socket(`fcntl()`)
          • Socket 超时设置
          • 综合实例(高并发 Echo 服务器)
        • 8. 实际应用案例
          • 简单的 HTTP Server
          • 实时聊天程序
        • 9. 总结
          • 文章知识结构
          • Socket 编程的核心要点
          • 进一步学习资源推荐


1. 简介
Socket 编程的基本概念

Socket 编程是指利用操作系统提供的套接字(Socket)接口,实现网络通信的编程方法。Socket 可以看作是两个网络应用程序之间进行双向通信的端点,类似于现实生活中的电话插座。它封装了底层网络协议的复杂性,为开发者提供了简单易用的接口,使得不同主机上的进程能够通过网络交换数据。

在 Socket 编程中,通信的两端通常分为客户端(Client)和服务器端(Server)。客户端主动发起连接请求,而服务器端被动监听并接受连接。Socket 支持多种协议,包括可靠的面向连接的 TCP(传输控制协议)和不可靠的无连接 UDP(用户数据报协议)。

网络通信模型(TCP/IP、UDP)
  1. TCP/IP 通信模型

    • TCP(Transmission Control Protocol):是一种面向连接的、可靠的传输协议。它通过三次握手建立连接,确保数据按序传输,并提供错误检测和重传机制。适用于需要高可靠性的应用,如网页浏览(HTTP)、文件传输(FTP)、电子邮件(SMTP)等。
    • IP(Internet Protocol):负责数据包的路由和寻址,确保数据能够从源主机传输到目标主机。
  2. UDP 通信模型

    • UDP(User Datagram Protocol):是一种无连接的、不可靠的传输协议。它不保证数据包的顺序或可靠性,但传输效率高,延迟低。适用于实时性要求高的应用,如视频流(RTP)、在线游戏、DNS 查询等。
Socket 在 Linux 中的角色

在 Linux 系统中,Socket 是内核提供的一种文件描述符(File Descriptor),开发者可以通过标准的文件 I/O 操作(如 readwrite)来读写 Socket。Linux 提供了丰富的系统调用(System Calls)支持 Socket 编程,例如:

  • socket():创建一个新的 Socket。
  • bind():将 Socket 绑定到一个特定的 IP 地址和端口。
  • listen():使服务器端 Socket 进入监听状态。
  • accept():接受客户端的连接请求。
  • connect():客户端发起连接请求。
  • send()/recv():发送和接收数据。

Linux 的 Socket 接口遵循 POSIX 标准,具有良好的跨平台兼容性,使得基于 Socket 开发的网络程序能够在不同 UNIX/Linux 系统上运行。此外,Linux 内核还支持多种 Socket 类型,如流式 Socket(SOCK_STREAM,对应 TCP)和数据报 Socket(SOCK_DGRAM,对应 UDP),以及原始 Socket(SOCK_RAW)用于直接访问底层协议。

Socket 编程是 Linux 网络应用程序开发的基础,广泛应用于 Web 服务器、数据库、即时通讯、分布式系统等领域。


2. Socket 类型与协议
  1. 流式 Socket (SOCK_STREAM,TCP)

    • 提供面向连接的、可靠的字节流传输服务
    • 保证数据按顺序到达且无重复
    • 典型应用场景:
      • Web 浏览 (HTTP/HTTPS)
      • 文件传输 (FTP)
      • 电子邮件 (SMTP/POP3)
    • 建立连接需要三次握手,断开需要四次挥手
    • TCP 头部包含序列号、确认号等控制字段
  2. 数据报 Socket (SOCK_DGRAM,UDP)

    • 提供无连接的、不可靠的数据报传输服务
    • 不保证数据顺序和可靠性,但传输效率高
    • 典型应用场景:
      • 实时音视频传输
      • DNS 查询
      • 在线游戏
    • 每个数据包独立路由,头部仅包含源/目标端口和长度
  3. 原始 Socket (SOCK_RAW)

    • 允许直接访问底层协议(如IP/ICMP)
    • 可以自定义协议头部字段
    • 典型用途:
      • 网络嗅探工具开发
      • 自定义协议实现
      • 网络安全测试
    • 需要管理员权限才能创建
    • 可以接收所有经过网卡的数据包(包括异常包)

注:选择 Socket 类型时应根据应用场景的需求权衡可靠性(TCP)与实时性(UDP)


3. 基本 Socket 函数

以下是网络编程中常用的基本 Socket 函数及其详细说明:

  1. socket():创建 Socket

    • 功能:创建一个新的 Socket 描述符,指定通信域(如 IPv4/IPv6)、Socket 类型(如流式 TCP 或数据报 UDP)和协议(通常为 0,表示自动选择)。
    • 示例:
      int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建 TCP Socket
      
  2. bind():绑定 IP 和端口

    • 功能:将 Socket 绑定到特定的 IP 地址和端口号,通常用于服务器端。
    • 示例:
      struct sockaddr_in serv_addr;
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = INADDR_ANY; // 绑定所有可用 IP
      serv_addr.sin_port = htons(8080); // 绑定端口 8080
      bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
      
  3. listen():监听连接(TCP)

    • 功能:将 Socket 设置为被动监听模式,等待客户端连接请求,仅适用于 TCP。
    • 参数:backlog 指定等待连接队列的最大长度。
    • 示例:
      listen(sockfd, 5); // 允许最多 5 个未处理的连接请求
      
  4. accept():接受连接(TCP)

    • 功能:从监听队列中接受一个客户端连接请求,返回一个新的 Socket 描述符用于通信。
    • 示例:
      struct sockaddr_in cli_addr;
      socklen_t clilen = sizeof(cli_addr);
      int newsockfd = accept(sockfd, (struct sockaddr*)&cli_addr, &clilen);
      
  5. connect():发起连接(TCP/UDP)

    • 功能:客户端调用此函数向服务器发起连接(TCP)或直接发送数据(UDP)。
    • 示例(TCP):
      struct sockaddr_in serv_addr;
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_port = htons(8080);
      inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
      connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
      
  6. send()/recv():数据收发(TCP)

    • 功能:在已建立的 TCP 连接上发送或接收数据。
    • 示例:
      char buffer[1024];
      recv(newsockfd, buffer, sizeof(buffer), 0); // 接收数据
      send(newsockfd, "Hello, client!", 14, 0); // 发送数据
      
  7. sendto()/recvfrom():数据收发(UDP)

    • 功能:用于无连接的 UDP 通信,指定目标地址或获取来源地址。
    • 示例:
      struct sockaddr_in cli_addr;
      char buffer[1024];
      socklen_t len = sizeof(cli_addr);
      recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&cli_addr, &len);
      sendto(sockfd, "Reply", 6, 0, (struct sockaddr*)&cli_addr, len);
      
  8. close():关闭 Socket

    • 功能:释放 Socket 资源,终止通信。
    • 示例:
      close(sockfd); // 关闭 Socket
      

这些函数是网络编程的基础,适用于构建客户端-服务器模型或点对点通信。


4. TCP Socket 编程示例

以下是一个完整的TCP服务器端实现示例,展示了基本的Socket编程流程,包括创建Socket、绑定端口、监听连接、收发数据等关键步骤:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define PORT 8080       // 定义服务器监听端口
#define BUFFER_SIZE 1024 // 定义缓冲区大小int main() {int server_fd, new_socket;struct sockaddr_in address;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};  // 初始化接收缓冲区char *response = "Hello from server"; // 定义服务器响应消息// 1. 创建Socket文件描述符// AF_INET表示IPv4协议,SOCK_STREAM表示TCP协议if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 2. 配置服务器地址结构address.sin_family = AF_INET;         // 使用IPv4地址族address.sin_addr.s_addr = INADDR_ANY; // 监听任意网卡地址address.sin_port = htons(PORT);       // 设置端口号(转换为网络字节序)// 3. 绑定Socket到指定IP和端口if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 4. 开始监听连接请求// 第二个参数3表示等待连接队列的最大长度if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);// 5. 接受客户端连接,创建新的Socket用于通信if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 6. 读取客户端发送的数据int valread = read(new_socket, buffer, BUFFER_SIZE);printf("Received from client: %s\n", buffer);// 7. 向客户端发送响应数据send(new_socket, response, strlen(response), 0);printf("Response sent to client\n");// 8. 关闭Socket连接close(new_socket);close(server_fd);return 0;
}

应用场景说明

  1. 这个示例可作为简单的TCP服务器基础框架
  2. 可用于开发网络聊天程序的基础通信模块
  3. 适用于需要实现基本请求-响应模型的网络服务
  4. 可作为学习网络编程的入门示例

扩展建议

  1. 可以添加多线程处理以支持多个客户端连接
  2. 可以增加错误处理的健壮性
  3. 可以扩展为支持HTTP协议的简单Web服务器
  4. 可以添加日志记录功能

注意事项

  1. 需要root权限才能绑定1024以下的端口
  2. 实际应用中应考虑设置SO_REUSEADDR选项
  3. 网络字节序转换(htons)是必须的
  4. 生产环境需要更完善的错误处理机制

5. UDP Socket 编程示例

UDP (User Datagram Protocol) 是一种无连接的传输协议,适用于对实时性要求高但允许少量丢包的应用场景,如视频会议、在线游戏等。下面是一个完整的UDP服务器实现示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>#define BUFFER_SIZE 1024
#define SERVER_PORT 8080int main() {int sockfd;struct sockaddr_in server_addr, client_addr;char buffer[BUFFER_SIZE];char *response_message = "Hello from UDP server";// 1. 创建Socket// AF_INET表示IPv4协议族// SOCK_DGRAM指定使用数据报套接字(UDP)// 0表示使用默认协议if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {perror("Socket creation failed");exit(EXIT_FAILURE);}// 2. 配置服务器地址结构memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;  // IPv4server_addr.sin_addr.s_addr = INADDR_ANY;  // 监听所有网卡server_addr.sin_port = htons(SERVER_PORT);  // 设置端口号,htons确保网络字节序// 3. 绑定Socket到指定端口if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Bind failed");close(sockfd);exit(EXIT_FAILURE);}printf("UDP server is running on port %d...\n", SERVER_PORT);// 4. 接收客户端数据socklen_t client_addr_len = sizeof(client_addr);ssize_t recv_len = recvfrom(sockfd, buffer, BUFFER_SIZE, 0,(struct sockaddr *)&client_addr, &client_addr_len);if (recv_len < 0) {perror("Receive failed");} else {buffer[recv_len] = '\0';  // 确保字符串以null结尾printf("Received %zd bytes from %s:%d\n", recv_len,inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));printf("Message: %s\n", buffer);}// 5. 发送响应数据if (sendto(sockfd, response_message, strlen(response_message), 0,(struct sockaddr *)&client_addr, client_addr_len) < 0) {perror("Send failed");}// 6. 关闭Socketclose(sockfd);return 0;
}

应用场景说明:

  1. 实时音视频传输:UDP的低延迟特性使其适合视频会议应用
  2. DNS查询:DNS协议通常使用UDP进行域名解析
  3. 在线游戏:多数网络游戏使用UDP传输玩家位置等实时数据

使用注意事项:

  1. UDP不保证数据包的顺序和可靠性
  2. 单个UDP数据包最大长度通常为65507字节(IPv4)
  3. 需要应用层自己实现超时重传等可靠性机制
  4. 可以使用setsockopt()设置接收缓冲区大小

该示例展示了UDP服务器端的基本工作流程:创建socket->绑定地址->接收数据->发送响应->关闭socket。客户端实现类似,只是不需要bind操作。


6. 高级 Socket 编程技术
多路复用(select()poll()epoll()

多路复用技术允许单个线程同时监控多个 Socket 文件描述符,提高网络程序的并发性能。

  • select():通过轮询方式检查多个文件描述符的状态,支持跨平台但有 1024 个描述符的限制。

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(sockfd, &read_fds);
    select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
    
  • poll():改进 select() 的限制,使用链表结构存储文件描述符,但效率仍依赖轮询。

    struct pollfd fds[1];
    fds[0].fd = sockfd;
    fds[0].events = POLLIN;
    poll(fds, 1, timeout_ms);
    
  • epoll()(Linux 特有):基于事件驱动的回调机制,适用于高并发场景。

    int epfd = epoll_create1(0);
    struct epoll_event ev;
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
    epoll_wait(epfd, events, MAX_EVENTS, timeout);
    
非阻塞 Socket(fcntl()

通过设置 Socket 为非阻塞模式,可以避免 accept()recv() 等操作阻塞线程。

int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
  • 应用场景:适用于需要异步处理连接或数据的程序,如游戏服务器、实时通信系统。
Socket 超时设置

可通过 setsockopt() 设置 Socket 操作的超时时间,防止长时间阻塞。

struct timeval timeout;
timeout.tv_sec = 5;  // 5秒超时
timeout.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
综合实例(高并发 Echo 服务器)

以下是一个结合 epoll() 和非阻塞 Socket 实现的 Echo 服务器框架:

// 初始化 epoll
int epfd = epoll_create1(0);
struct epoll_event ev, events[MAX_EVENTS];// 设置监听 Socket 为非阻塞
fcntl(listen_fd, F_SETFL, O_NONBLOCK);
ev.events = EPOLLIN | EPOLLET;  // 边缘触发模式
ev.data.fd = listen_fd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &ev);while (1) {int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);for (int i = 0; i < nfds; i++) {if (events[i].data.fd == listen_fd) {// 处理新连接int conn_fd = accept(listen_fd, ...);fcntl(conn_fd, F_SETFL, O_NONBLOCK);ev.events = EPOLLIN | EPOLLET;ev.data.fd = conn_fd;epoll_ctl(epfd, EPOLL_CTL_ADD, conn_fd, &ev);} else {// 处理客户端数据char buffer[1024];ssize_t n = recv(events[i].data.fd, buffer, sizeof(buffer), 0);if (n > 0) {send(events[i].data.fd, buffer, n, 0);} else {close(events[i].data.fd);}}}
}
  • 适用场景:适用于需要高并发、低延迟的网络服务,如即时聊天、在线游戏服务器等。

8. 实际应用案例
简单的 HTTP Server

使用 Node.js 内置的 http 模块可以快速搭建一个基础的 HTTP 服务器。以下是一个完整的示例代码:

const http = require('http');const server = http.createServer((req, res) => {res.writeHead(200, { 'Content-Type': 'text/plain' });res.end('Hello, Node.js HTTP Server!\n');
});server.listen(3000, () => {console.log('Server running at http://localhost:3000/');
});

功能说明:

  1. http.createServer 创建一个服务器实例,接收请求并返回响应。
  2. res.writeHead 设置 HTTP 响应状态码和头部信息。
  3. res.end 发送响应内容并结束请求。
  4. server.listen 启动服务器,监听 3000 端口。

应用场景:

  • 快速测试 API 接口
  • 本地开发环境模拟后端服务
  • 学习 HTTP 协议和请求/响应机制
实时聊天程序

使用 Node.js 结合 socket.io 库可以轻松实现一个实时聊天应用。以下是关键实现步骤:

  1. 安装依赖

    npm install socket.io express
    
  2. 服务端代码

    const express = require('express');
    const socketIO = require('socket.io');const app = express();
    const server = app.listen(3000, () => {console.log('Chat server running on port 3000');
    });const io = socketIO(server);io.on('connection', (socket) => {console.log('New user connected');socket.on('chat message', (msg) => {io.emit('chat message', msg); // 广播消息给所有客户端});socket.on('disconnect', () => {console.log('User disconnected');});
    });
    
  3. 客户端代码(HTML + JavaScript)

    <script src="/socket.io/socket.io.js"></script>
    <script>const socket = io();socket.on('chat message', (msg) => {// 接收并显示消息const messages = document.getElementById('messages');messages.innerHTML += `<li>${msg}</li>`;});// 发送消息document.getElementById('send').addEventListener('click', () => {const message = document.getElementById('message').value;socket.emit('chat message', message);});
    </script>
    

核心功能:

  • 基于 WebSocket 实现双向实时通信
  • socket.emit 发送消息,socket.on 监听事件
  • 自动处理连接/断开事件

应用场景:

  • 在线客服系统
  • 多人在线游戏聊天
  • 协同办公工具的实时通知

9. 总结
文章知识结构

在这里插入图片描述

Socket 编程的核心要点
  1. 基本概念

    • Socket 本质:Socket 是网络通信的端点,用于实现不同主机或同一主机上不同进程间的数据传输。
    • 通信模型:常见的通信模型包括 TCP(面向连接、可靠传输)和 UDP(无连接、不可靠但高效)。
    • IP 地址与端口:Socket 通信依赖 IP 地址标识主机,端口号标识具体服务(如 HTTP 默认端口 80)。
  2. 核心步骤(以 TCP 为例)

    • 服务端流程
      1. 创建 Socket(socket())。
      2. 绑定 IP 和端口(bind())。
      3. 监听连接请求(listen())。
      4. 接受客户端连接(accept())。
      5. 收发数据(send()/recv())。
      6. 关闭连接(close())。
    • 客户端流程
      1. 创建 Socket。
      2. 连接服务端(connect())。
      3. 收发数据。
      4. 关闭连接。
  3. 关键问题与优化

    • 粘包处理:TCP 是字节流协议,需自定义协议(如固定长度头或分隔符)解决粘包问题。
    • 并发模型:多线程/多进程(如 fork())、IO 多路复用(如 select()/epoll)可提升服务端并发能力。
    • 错误处理:需捕获 ECONNRESET(连接重置)等异常,确保程序健壮性。
进一步学习资源推荐
  1. 书籍
    • 《UNIX 网络编程》(W. Richard Stevens):经典教材,涵盖 Socket 编程深度实践。
    • 《TCP/IP 详解 卷1:协议》(W. Richard Stevens):深入理解底层协议栈。
  2. 在线资源
    • Beej’s Guide to Network Programming:免费、易懂的 Socket 编程教程(中英文版均有)。
    • Linux man 手册:命令行输入 man socket 查看系统调用文档。
  3. 实践方向
    • 实现简易聊天程序(TCP/UDP 双版本)。
    • 结合 HTTP 协议实现微型 Web 服务器(如解析 GET 请求返回文件)。
    • 学习开源项目(如 Nginx、Redis)的网络模块设计。

(注:示例代码与工具推荐可根据读者基础选择性补充。)


研究学习不易,点赞易。
工作生活不易,收藏易,点收藏不迷茫 :)


相关文章:

  • 【AI论文】工具之星(Tool-Star):通过强化学习赋能具备大型语言模型(LLM)思维的多工具推理器
  • 一体化雷达波明渠流量计简介
  • 【数据集】中国大陆城市建筑楼面面积高分辨率数据集(2017年)
  • Vue 3 路由传参使用指南
  • JavaSE核心知识点03高级特性03-04(Lambda表达式)
  • 【RocketMQ 生产者和消费者】- 生产者启动源码 - MQClientInstance 定时任务(4)
  • 开盘啦 APP 抓包 逆向分析
  • 真实案例拆解:智能AI客服系统中的两类缓存协同
  • 高分辨率北半球多年冻土数据集(2000-2016)
  • 7.1查找的基本概念
  • 第307个VulnHub靶场演练攻略Corrosion: 2
  • 可编程运动控制器行业2025数据分析报告
  • SQL每日一题(5)
  • 模拟电子技术基础----绪论
  • 【刷题】质数因子
  • JDK21深度解密 Day 2:虚拟线程入门与基础应用
  • lc hot 100之:环形链表
  • Redis 常用命令
  • 005 深度优先搜索(DFS)算法详解:图解+代码+经典例题
  • Linux命令简介
  • 制作公司网站 黑龙江/常德seo公司
  • bc网站搭建网站开发/长沙优化网站厂家
  • 杭州知名app技术开发公司/网站优化排名的方法
  • 镇江网站推广/如何注册一个自己的网站
  • 华为手机开发者模式怎么关闭/文山seo公司
  • wordpress网站数据迁移/seo文章是什么意思