一、前言
1. 客户端代码(tcpclient/program.cs)
该代码实现了一个基础的 tcp 客户端程序,核心逻辑是与指定 ip 和端口的 tcp 服务器建立连接,向服务器发送控制台输入的字符串数据,并接收服务器的响应数据,最后释放连接资源。核心步骤包括:
- 定义服务器 ip(
172.16.0.14)和端口(9898); - 通过
tcpclient发起连接(底层触发 tcp 三次握手); - 获取
networkstream网络流,完成数据的发送与接收; - 异常捕获与资源释放(关闭流和连接,底层触发 tcp 四次挥手)。
2. 服务器端代码(tcpserver/program.cs)
该代码实现了一个基础的 tcp 服务器程序,核心逻辑是监听指定端口(9898),等待并接收客户端连接,读取客户端发送的数据,向客户端返回响应,同时保留了另一版监听8888端口的注释代码。核心步骤包括:
- 绑定并监听
9898端口; - 阻塞等待客户端连接(
accepttcpclient()); - 获取
networkstream,循环读取客户端数据并返回响应; - 异常捕获与资源释放。
二、网络编程核心概念与原理
1. 什么是网络编程?
网络编程是指编写运行在不同设备(计算机、嵌入式设备等)上的程序,通过网络协议实现设备间的数据通信。简单来说,就是让两台 / 多台设备 “对话” 的编程方式。其核心目标是跨设备的数据传输与交互,依赖 tcp/ip 协议族等基础网络协议,tcp 是其中最常用的 “可靠传输协议”。
2. tcp 网络编程核心原理
tcp(transmission control protocol,传输控制协议)是一种面向连接、可靠、基于字节流的传输层协议,也是本次示例代码的核心依赖:
(1)核心特性
- 面向连接:通信前必须通过 “三次握手” 建立连接,通信结束通过 “四次挥手” 断开连接;
- 可靠性:通过序列号、确认应答、重传机制保证数据不丢失、不重复、按序到达;
- 字节流:数据以 “字节” 为单位连续传输,无边界(需通过缓冲区和读取长度判断数据范围)。
(2)三次握手(建立连接)
类比生活中 “打电话”:
客户端 → 服务器:“喂,能听到吗?”(syn 报文,请求建立连接);
服务器 → 客户端:“能听到,你能听到我吗?”(syn+ack 报文,确认请求并反问);
客户端 → 服务器:“能听到,开始沟通吧!”(ack 报文,连接建立)。
代码中
new tcpclient(server, port)
是客户端发起第一次握手,
accepttcpclient()
是服务器完成三次握手的关键操作。
(3)四次挥手(断开连接)
类比 “挂电话”:
- 主动方 → 被动方:“我说完了,准备挂了”(fin 报文,请求断开);
- 被动方 → 主动方:“知道你说完了,我确认下数据”(ack 报文,确认断开请求);
- 被动方 → 主动方:“我也说完了,你可以挂了”(fin 报文,告知数据发完);
- 主动方 → 被动方:“好的,挂了”(ack 报文,断开连接)。
代码中
stream.close()
+
client.close()
是触发四次挥手的核心操作。
三、完整示例代码(可直接运行)
1. tcp 客户端完整代码
using system;
using system.net.sockets;
using system.text;
namespace tcpclient
{
internal class program
{
static void main(string[] args)
{
// 定义服务器的ip地址和端口号
string serverip = "172.16.0.14"; // 替换为实际服务器ip(本机测试可填127.0.0.1)
int serverport = 9898;
tcpclient tcpclient = null;
networkstream networkstream = null;
try
{
// 1. 建立tcp连接(底层三次握手)
tcpclient = new tcpclient(serverip, serverport);
console.writeline($"成功连接到服务器 {serverip}:{serverport}");
// 2. 获取网络流(用于数据收发)
networkstream = tcpclient.getstream();
// 3. 发送数据到服务器
console.writeline("请输入要发送的消息:");
string sendmessage = console.readline();
byte[] senddata = encoding.utf8.getbytes(sendmessage);
networkstream.write(senddata, 0, senddata.length);
console.writeline($"客户端已发送:{sendmessage}");
// 4. 接收服务器响应
byte[] receivebuffer = new byte[256]; // 定义缓冲区存储接收的数据
int receivebytes = networkstream.read(receivebuffer, 0, receivebuffer.length);
string receivemessage = encoding.utf8.getstring(receivebuffer, 0, receivebytes);
console.writeline($"客户端收到服务器响应:{receivemessage}");
}
catch (exception ex)
{
console.writeline($"连接/通信异常:{ex.message}");
}
finally
{
// 5. 释放资源(底层四次挥手)
if (networkstream != null) networkstream.close();
if (tcpclient != null) tcpclient.close();
}
console.writeline("客户端程序结束,按任意键退出...");
console.readkey();
}
}
}2. tcp 服务器完整代码
using system;
using system.net;
using system.net.sockets;
using system.text;
namespace tcpserver
{
internal class program
{
static void main(string[] args)
{
// 定义监听端口
int listenport = 9898;
tcplistener tcplistener = null;
tcpclient client = null;
networkstream networkstream = null;
try
{
// 1. 初始化监听器并启动监听
tcplistener = new tcplistener(ipaddress.any, listenport);
tcplistener.start();
console.writeline($"服务器已启动,正在监听端口 {listenport}...");
// 2. 循环等待客户端连接(单线程,仅处理一个客户端)
while (true)
{
// 阻塞等待客户端连接
client = tcplistener.accepttcpclient();
console.writeline("有客户端成功连接!");
// 3. 获取网络流,处理数据交互
networkstream = client.getstream();
byte[] receivebuffer = new byte[256];
int readbytes;
// 循环读取客户端发送的数据(直到客户端断开)
while ((readbytes = networkstream.read(receivebuffer, 0, receivebuffer.length)) != 0)
{
// 解析接收的字节数据为字符串
string receivemessage = encoding.utf8.getstring(receivebuffer, 0, readbytes);
console.writeline($"服务器收到客户端数据:{receivemessage}");
// 4. 向客户端发送响应
string responsemessage = "已收到客户端消息";
byte[] responsedata = encoding.utf8.getbytes(responsemessage);
networkstream.write(responsedata, 0, responsedata.length);
console.writeline($"服务器已发送响应:{responsemessage}");
}
}
}
catch (exception ex)
{
console.writeline($"服务器异常:{ex.tostring()}");
}
finally
{
// 5. 释放资源
if (networkstream != null) networkstream.close();
if (client != null) client.close();
if (tcplistener != null) tcplistener.stop();
}
console.writeline("服务器程序结束,按任意键退出...");
console.readkey();
}
}
}四、代码运行说明
1. 环境准备
- 开发工具:visual studio 2019/2022(或其他支持.net framework/.net core 的 ide);
- 框架版本:.net framework 4.5+ 或 .net core 3.1+(代码兼容)。
2. 运行步骤
先运行tcp 服务器程序:控制台输出 “服务器已启动,正在监听端口 9898...”;
再运行
tcp 客户端程序
- 客户端控制台提示 “成功连接到服务器”;
- 输入任意字符串(如 “hello server!”)并回车;
- 客户端显示 “已发送”,并接收服务器响应 “已收到客户端消息”;
- 服务器端显示 “有客户端成功连接”,并打印收到的 “hello server!”,同时提示 “已发送响应”。
五、生活例子理解 tcp 网络编程
例子 1:快递配送(类比 tcp 可靠传输)
- 服务器 = 快递公司仓库,客户端 = 你(收件人);
- 建立连接(三次握手):你下单(syn)→ 仓库确认接单(syn+ack)→ 你确认(ack);
- 数据传输(可靠):仓库打包商品(数据转字节)→ 快递员配送(网络传输)→ 你签收(确认应答);若快递丢失,仓库重发(重传机制);
- 断开连接(四次挥手):你确认收完货(fin)→ 仓库确认(ack)→ 仓库确认无遗漏(fin)→ 你确认(ack),交易结束。
例子 2:餐厅点餐(类比客户端 - 服务器交互)
- 服务器 = 餐厅(固定位置,监听 “点餐请求”),客户端 = 顾客;
- 监听:餐厅开门(启动
tcplistener),等待顾客; - 建立连接:你走进餐厅(客户端发起连接),服务员接待(
accepttcpclient); - 数据交互:你说 “要牛肉面”(客户端发送数据)→ 服务员回复 “好的”(服务器响应);
- 断开连接:你吃完离开(关闭连接),服务员结束服务(释放资源)。
六、代码拓展与注意事项
1. 代码局限性(进阶优化方向)
- 单线程服务器:示例中服务器只能处理一个客户端,实际开发需用多线程 / 异步(如
task.run、async/await)支持多客户端并发; - 无粘包处理:tcp 是字节流,若客户端连续发数据,服务器可能 “粘包”(一次读取多条数据),需自定义协议(如在数据前加长度前缀);
- 硬编码 ip / 端口:实际项目应通过配置文件(如
appsettings.json)读取,提高灵活性; - 无断线重连:客户端断开后需手动重启,可增加重连逻辑提升健壮性。
2. 实际应用场景
tcp 网络编程广泛用于:
- 即时通讯(微信、qq 的核心通信);
- 文件传输(ftp、网盘上传下载);
- 数据库连接(mysql、sql server 的客户端 - 服务器通信);
- 游戏联机(多人游戏的实时数据交互)。
七、总结
本文的示例代码实现了最基础的 tcp 客户端 - 服务器通信:客户端发起连接并发送字符串,服务器监听连接、接收数据并返回响应。背后的核心是 tcp 协议的 “面向连接、可靠性”,通过三次握手建立连接、四次挥手断开连接,借助networkstream完成字节级的数据传输。
生活中的 “打电话、快递、点餐” 等例子能帮助理解:网络编程本质是让不同设备按约定规则 “对话”,而 tcp 是保证这次 “对话” 稳定、可靠的 “沟通规则”。初学者掌握基础流程后,可进一步学习多线程服务器、粘包处理、异步 io 等进阶内容,实现更贴近实际场景的网络程序。
到此这篇关于c#基于tcp通信协议的实现示例的文章就介绍到这了,更多相关c# tcp通信协议内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论