Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录
- Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
- 前言
- 一、网络通信基础概念
- 二、服务端与客户端的完整流程图解
- 三、每一步的详细讲解和代码示例
- 1. 创建Socket(服务端和客户端都要)
- 2. 绑定本地地址和端口(服务端用bind)
- 3. 设置监听(服务端listen)
- 4. 等待并接受连接(服务端accept)
- 5. 客户端发起连接(connect)
- 6. 数据收发(read/write)
- 7. 关闭socket
- 四、流程图&示意
- 五、错误处理机制(errno、perror、strerror)
- 六、完整最简服务端和客户端代码范例
- 1. 服务端示例
- 2. 客户端示例
- 七、通用经验和常见问题排查
- 八、图解数据流和socket关系
- 九、总结
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
前言
本文面向初学者,目标是让你“一看就懂、能马上动手实践”。从零讲起,手把手梳理服务端与客户端的构建全过程,每个函数、参数、典型用法、数据流向、底层机制、注意事项全部细细拆解,让你彻底明白如何让两个程序通过网络可靠通信。
一、网络通信基础概念
-
什么是Socket?
- Socket(套接字)是操作系统为进程之间通过网络发送和接收数据而提供的一套“接口”。
- 类比现实世界,就是一台机器(主机)上的“电话插孔”,只有插上电话线(建立连接)你们才能说话。
- Socket抽象了所有底层的网络细节,为开发者提供了“像读写文件一样”进行网络通信的方式。
-
IP和端口
- IP:主机的唯一网络地址,相当于“电话号码”。
- 端口:主机内部区分不同网络服务的编号,相当于“分机号”。
-
TCP通信流程(面向连接)
- 服务端先开启,监听一个IP+端口。
- 客户端主动连接服务端的IP+端口。
- 建立连接(三次握手)。
- 双方可以互相发送和接收数据。
- 通信完成后关闭连接。
二、服务端与客户端的完整流程图解
服务端主要流程:
- 创建Socket
- 绑定本地IP和端口(bind)
- 设置为监听状态(listen)
- 死循环等待客户端连接(accept)
- 收发数据(read/write)
- 关闭通信(close)
客户端主要流程:
- 创建Socket
- 配置服务器IP和端口
- 发起连接请求(connect)
- 收发数据(read/write)
- 关闭通信(close)
三、每一步的详细讲解和代码示例
1. 创建Socket(服务端和客户端都要)
作用:告诉内核“我要用网络通信”,创建一个通信端点。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
- domain:协议族,常用
AF_INET
(IPv4)。 - type:套接字类型,常用
SOCK_STREAM
(TCP,可靠流)。 - protocol:协议,通常填0,表示由系统自动选择。
举例:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {perror("socket error");exit(1);
}
- 返回值:成功时是一个“文件描述符”,失败返回-1。
2. 绑定本地地址和端口(服务端用bind)
作用:明确告诉操作系统“我用哪个IP+端口”来等待客户端连接。只有bind了,别人才能找到你。
#include <netinet/in.h>
#include <string.h>
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; // 协议族
servaddr.sin_port = htons(8888); // 端口(本地字节序转网络字节序)
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 本机所有IPint ret = bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (ret == -1) {perror("bind error");exit(1);
}
htons
/htonl
是将主机字节序转为网络字节序(大端)。INADDR_ANY
让你的服务监听本机所有网卡(IP)。
3. 设置监听(服务端listen)
作用:让socket进入“监听”状态,准备接收连接请求。
int listen(int sockfd, int backlog);
- sockfd:刚才创建并bind过的socket。
- backlog:内核排队等待连接的最大数量。
举例:
if (listen(sockfd, 128) == -1) {perror("listen error");exit(1);
}
- 这时操作系统会帮你排队管理那些“想要连你”的客户端。
4. 等待并接受连接(服务端accept)
作用:阻塞等待客户端“来电”,接听一个新连接,为每个连接分配一个新的socket。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd:监听socket。
- addr:传出参数,获取客户端的IP+端口等信息。
- addrlen:addr结构体大小(调用前要赋初值)。
举例:
struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr*)&cliaddr, &cliaddr_len);
if (connfd == -1) {perror("accept error");continue; // 或exit(1)
}
- connfd:每个客户端连接都会分配一个新socket,独立通信。
- 注意:监听socket(sockfd)只负责“等电话”,不能直接收发数据,后面通信都用connfd。
5. 客户端发起连接(connect)
作用:主动“打电话”给服务端,发起三次握手,连接指定IP+端口。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
客户端配置服务器地址:
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(8888);
inet_pton(AF_INET, "192.168.1.100", &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("connect error");exit(1);
}
- inet_pton 用于将点分十进制IP字符串转为网络字节序整数。
6. 数据收发(read/write)
作用:收发字节流数据,就像读写文件一样。
write()
发送数据到对方read()
从对方接收数据
例子(双方都类似):
char buf[1024];
// 发送
write(sockfd, "hello", 5);
// 接收
int n = read(sockfd, buf, sizeof(buf)-1);
if (n > 0) {buf[n] = '\0';printf("收到: %s\n", buf);
}
- 注意:read和write返回值要判断,<=0说明对方关闭了连接或出错。
- 服务器处理建议:每个连接完成后记得close(connfd)。
7. 关闭socket
作用:释放系统资源,断开连接。
close(sockfd); // 对服务端监听socket、通信socket、客户端socket都适用
四、流程图&示意
服务器端流程 客户端流程
--------------------------------------------------------
socket() socket()| |
bind() connect()| |
listen() || ---------三次握手
accept() <--------+ || | |
read()/write() <---+---> read()/write()| | |
close() close() close()
五、错误处理机制(errno、perror、strerror)
- 每一步系统调用都可能出错,一定要检查返回值(-1 代表失败)。
- 出错后操作系统会设置errno变量(int类型,存放错误码)。
- perror(“描述”) 会直接把你的描述和错误原因一起打印到标准错误输出。
- strerror(errno) 把错误码转为字符串,可以写日志或文件。
例子:
if (bind(sockfd, ...) == -1) {perror("bind error");// fprintf(logfile, "bind error: %s\n", strerror(errno));exit(1);
}
- 建议每个步骤都这样处理,排查故障时一清二楚。
六、完整最简服务端和客户端代码范例
1. 服务端示例
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>#define SERVER_PORT 8888int main() {int listenfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERVER_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("bind error");exit(1);}if (listen(listenfd, 128) == -1) {perror("listen error");exit(1);}printf("Server is listening...\n");while (1) {struct sockaddr_in cliaddr;socklen_t cliaddr_len = sizeof(cliaddr);int connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &cliaddr_len);if (connfd == -1) {perror("accept error");continue;}char buf[1024];int n = read(connfd, buf, sizeof(buf)-1);if (n > 0) {buf[n] = '\0';printf("client says: %s\n", buf);write(connfd, buf, n); // 回显}close(connfd);}close(listenfd);return 0;
}
2. 客户端示例
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>#define SERVER_PORT 8888
#define SERVER_IP "127.0.0.1"int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in servaddr;memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERVER_PORT);inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {perror("connect error");exit(1);}char sendbuf[1024] = "hello server";write(sockfd, sendbuf, strlen(sendbuf));char recvbuf[1024];int n = read(sockfd, recvbuf, sizeof(recvbuf)-1);if (n > 0) {recvbuf[n] = '\0';printf("server says: %s\n", recvbuf);}close(sockfd);return 0;
}
七、通用经验和常见问题排查
- 端口被占用,bind出错:先用
netstat -ntlp
查端口占用,或改端口再试。 - connect失败:IP、端口写错?服务器没开?防火墙拦截?
- read/write出错或为0:对方关闭了连接,需及时close。
- 每个连接用独立socket,主循环不要关listenfd。
- 大项目建议引入多线程或select/epoll提升并发能力。
八、图解数据流和socket关系
| 客户端 | 网络 | 服务端 |
+--------+---------------+-----------------------+
| | ---connect--> | [listenfd] |
| | <---三次握手--| |
| | | accept()产生[connfd] |
| |<->read/write<>| [connfd]<->[listenfd] |
| | ---close----->| [connfd closed] |
- listenfd负责排队监听,不用于通信。connfd负责与客户端通信。
九、总结
- 先socket(),再bind()(服务端),然后listen(),accept()连接,read/write通信,close()结束。客户端用connect()主动连接。
- 每一步都检查错误,配合perror/strerror打印详细信息,便于调试和维护。
- IP、端口、字节序、缓冲区管理要细心。
- 建议将代码拆分成模块,错误处理、日志、收发通信各自封装。
只要理解并掌握上面每一步、每个关键函数的使用,你就能独立搭建出稳定可靠的C语言网络通信程序!每次遇到问题都可以翻回来看,一步步排查流程,问题迎刃而解。
相关文章:
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...

C++使用 new 来创建动态数组
问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

佰力博科技与您探讨热释电测量的几种方法
热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...

HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

算法岗面试经验分享-大模型篇
文章目录 A 基础语言模型A.1 TransformerA.2 Bert B 大语言模型结构B.1 GPTB.2 LLamaB.3 ChatGLMB.4 Qwen C 大语言模型微调C.1 Fine-tuningC.2 Adapter-tuningC.3 Prefix-tuningC.4 P-tuningC.5 LoRA A 基础语言模型 A.1 Transformer (1)资源 论文&a…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...