文章的目的为了记录使用C# 开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。
相关链接:
推荐链接:
开源 C# .net mvc 开发(一)WEB搭建_c#部署web程序-CSDN博客
开源 C# .net mvc 开发(二)网站快速搭建_c#网站开发-CSDN博客
开源 C# .net mvc 开发(三)WEB内外网访问-CSDN博客
开源 C# .net mvc 开发(四)工程结构、页面提交以及显示-CSDN博客
开源 C# .net mvc 开发(五)常用代码快速开发_c# mvc开发-CSDN博客
开源 C# .net mvc 开发(六)发送邮件、定时以及CMD编程-CSDN博客
开源 C# .net mvc 开发(七)动态图片、动态表格和json数据生成-CSDN博客
开源 C# .net mvc 开发(八)IIS Express轻量化Web服务器的配置和使用-CSDN博客
开源 C# .net mvc 开发(九)websocket--服务器与客户端的实时通信-CSDN博客
本章节主要内容是:TCP客户端应用程序,基于Windows Forms实现。这个TCP客户端实现了完整的连接管理、数据收发和错误处理机制,是一个功能完备的网络通信客户端。
目录:
1.源码分析
2.所有源码
3.效果演示
一、源码分析
- 构造函数和初始化 Form1()
功能:窗体初始化时自动获取并显示本机IP地址。
public Form1()
{
InitializeComponent();
UpdateLocalIPInfo(); // 初始化时获取本地IP信息
}
- 本地IP信息获取 UpdateLocalIPInfo()
技术要点:
使用Dns.GetHostEntry()获取本机所有网络接口的IP地址
过滤出IPv4地址(AddressFamily.InterNetwork)
异常处理确保程序稳定性
private void UpdateLocalIPInfo()
{
try
{
string localIP = GetLocalIPAddress(); // 获取本地IPv4地址
txtClientIP.Text = localIP; // 显示在文本框
}
catch (Exception ex)
{
txtClientIP.Text = "获取失败";
AddLog($"获取本地IP失败: {ex.Message}");
}
}
private string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork) // 只获取IPv4地址
{
return ip.ToString();
}
}
throw new Exception("未找到IPv4地址");
}
- 连接服务器 ConnectToServer()
关键特性:
异步连接:使用BeginConnect避免UI线程阻塞
输入验证:严格检查IP和端口格式
UI状态管理:及时更新按钮和状态显示
private void ConnectToServer()
{
try
{
// 输入验证
if (!IPAddress.TryParse(txtServerIP.Text, out IPAddress ipAddress))
{
MessageBox.Show("请输入有效的服务器IP地址");
return;
}
if (!int.TryParse(txtServerPort.Text, out int port) || port < 1 || port > 65535)
{
MessageBox.Show("请输入有效的端口号(1-65535)");
return;
}
// 异步连接服务器
tcpClient = new TcpClient();
tcpClient.BeginConnect(ipAddress, port, new AsyncCallback(ConnectCallback), tcpClient);
lblStatus.Text = "连接中...";
btnConnect.Enabled = false; // 防止重复点击
}
catch (Exception ex)
{
MessageBox.Show($"连接失败: {ex.Message}");
lblStatus.Text = "连接失败";
btnConnect.Enabled = true;
}
}
- 连接回调 ConnectCallback(IAsyncResult ar)
重要功能:
连接完成:通过EndConnect完成异步连接操作
本地端口获取:显示客户端使用的本地端口号
自动重连机制:在连接失败时自动尝试重新连接
private void ConnectCallback(IAsyncResult ar)
{
try
{
TcpClient client = (TcpClient)ar.AsyncState;
client.EndConnect(ar); // 完成异步连接
// 连接成功处理
isConnected = true;
clientStream = client.GetStream();
reconnectAttempts = 0; // 重置重连计数器
// 获取本地端口号
string localPort = ((IPEndPoint)client.Client.LocalEndPoint).Port.ToString();
Invoke(new Action(() =>
{
txtClientPort.Text = localPort;
lblStatus.Text = $"已连接到 {txtServerIP.Text}:{txtServerPort.Text}";
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
btnSend.Enabled = true;
AddLog($"连接成功 - 本地端口: {localPort}");
}));
// 启动数据接收线程
receiveThread = new Thread(new ThreadStart(ReceiveData));
receiveThread.IsBackground = true;
receiveThread.Start();
}
catch (Exception ex)
{
Invoke(new Action(() =>
{
lblStatus.Text = "连接失败";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
AddLog($"连接失败: {ex.Message}");
// 自动重连逻辑
if (autoReconnect && reconnectAttempts < MAX_RECONNECT_ATTEMPTS)
{
reconnectAttempts++;
AddLog($"尝试自动重连 ({reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})...");
Thread.Sleep(3000); // 等待3秒
ConnectToServer(); // 重新连接
}
}));
}
}
- 数据接收 ReceiveData()
异常处理策略:
IOException:网络连接问题
ObjectDisposedException:资源已被释放
通用异常:其他未知错误
private void ReceiveData()
{
byte[] buffer = new byte[4096]; // 4KB缓冲区
int bytesRead;
while (isConnected && tcpClient != null && tcpClient.Connected)
{
try
{
// 阻塞读取数据
bytesRead = clientStream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0) // 服务器主动关闭连接
{
HandleDisconnection("服务器关闭了连接");
break;
}
string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Invoke(new Action(() =>
{
AddLog($"接收: {receivedData}");
}));
}
catch (IOException)
{
// 网络连接异常
HandleDisconnection("连接被中断");
break;
}
catch (ObjectDisposedException)
{
// 流已被关闭
break;
}
catch (Exception ex)
{
if (isConnected)
{
Invoke(new Action(() => AddLog($"接收数据时出错: {ex.Message}")));
}
break;
}
}
}
- 连接断开处理 HandleDisconnection(string reason)
自动重连机制:
最大重试次数:5次(MAX_RECONNECT_ATTEMPTS)
重连间隔:3秒
条件控制:通过autoReconnect复选框启用/禁用
private void HandleDisconnection(string reason)
{
if (!isConnected) return;
isConnected = false;
Invoke(new Action(() =>
{
lblStatus.Text = "连接已断开";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
btnSend.Enabled = false;
AddLog($"连接断开: {reason}");
// 自动重连逻辑
if (autoReconnect && reconnectAttempts < MAX_RECONNECT_ATTEMPTS)
{
reconnectAttempts++;
AddLog($"尝试自动重连 ({reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})...");
Thread.Sleep(3000);
ConnectToServer();
}
}));
// 资源清理
try
{
clientStream?.Close();
tcpClient?.Close();
}
catch { }
}
- 主动断开连接 Disconnect()
资源管理:
显式关闭网络流和客户端对象
终止后台接收线程
禁用自动重连功能
private void Disconnect()
{
try
{
isConnected = false;
autoReconnect = false; // 禁用自动重连
// 终止接收线程
receiveThread?.Abort();
receiveThread = null;
// 关闭网络资源
clientStream?.Close();
tcpClient?.Close();
Invoke(new Action(() =>
{
lblStatus.Text = "已断开连接";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
btnSend.Enabled = false;
txtClientPort.Text = ""; // 清空端口显示
AddLog("主动断开连接");
}));
}
catch (Exception ex)
{
Invoke(new Action(() => AddLog($"断开连接时出错: {ex.Message}")));
}
}
- 数据发送 SendData()
发送特性:
连接状态实时验证
UTF-8编码支持中文
发送后自动清空输入框
private void SendData()
{
// 连接状态检查
if (!isConnected || tcpClient == null || !tcpClient.Connected)
{
MessageBox.Show("未连接到服务器");
return;
}
if (string.IsNullOrWhiteSpace(txtSend.Text))
{
MessageBox.Show("请输入要发送的数据");
return;
}
try
{
byte[] data = Encoding.UTF8.GetBytes(txtSend.Text);
clientStream.Write(data, 0, data.Length);
clientStream.Flush(); // 立即发送
Invoke(new Action(() =>
{
AddLog($"发送: {txtSend.Text}");
txtSend.Clear(); // 清空发送框
}));
}
catch (Exception ex)
{
Invoke(new Action(() =>
{
AddLog($"发送失败: {ex.Message}");
HandleDisconnection("发送数据时出错");
}));
}
}
- 键盘快捷键 txtSend_KeyPress()
用户体验优化:
支持Ctrl+Enter多行发送
支持Enter键快速发送
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
// Ctrl+Enter 发送
if (e.KeyChar == '\r' && Control.ModifierKeys == Keys.Control)
{
e.Handled = true;
SendData();
}
// Enter 发送(单行模式)
else if (e.KeyChar == '\r' && !txtSend.Multiline)
{
e.Handled = true;
SendData();
}
}
- 日志记录 AddLog(string message)
线程安全:使用Invoke确保跨线程UI操作安全。
private void AddLog(string message)
{
if (txtReceive.InvokeRequired)
{
txtReceive.Invoke(new Action<string>(AddLog), message);
}
else
{
txtReceive.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\r\n");
txtReceive.ScrollToCaret(); // 自动滚动到底部
}
}
二、所有源码
Form1.Designer.cs界面代码
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ClientForm
{
partial class Form1
{
/*
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Text = "Form1";
}
#endregion
*/
private System.ComponentModel.IContainer components = null;
private TextBox txtServerIP;
private TextBox txtServerPort;
private Button btnConnect;
private Button btnDisconnect;
private TextBox txtSend;
private Button btnSend;
private TextBox txtReceive;
private Label lblStatus;
private Label label1;
private Label label2;
private Label label3;
private Label label4;
private CheckBox chkAutoReconnect;
private Button btnClear;
private TextBox txtClientIP;
private TextBox txtClientPort;
private Label label5;
private Label label6;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.txtServerIP = new TextBox();
this.txtServerPort = new TextBox();
this.btnConnect = new Button();
this.btnDisconnect = new Button();
this.txtSend = new TextBox();
this.btnSend = new Button();
this.txtReceive = new TextBox();
this.lblStatus = new Label();
this.label1 = new Label();
this.label2 = new Label();
this.label3 = new Label();
this.label4 = new Label();
this.chkAutoReconnect = new CheckBox();
this.btnClear = new Button();
this.txtClientIP = new TextBox();
this.txtClientPort = new TextBox();
this.label5 = new Label();
this.label6 = new Label();
// 窗体设置
this.Text = "TCP客户端";
this.Size = new Size(700, 600);
this.StartPosition = FormStartPosition.CenterScreen;
// 服务器IP标签和文本框
this.label1.Location = new Point(20, 20);
this.label1.Size = new Size(80, 20);
this.label1.Text = "服务器IP:";
this.txtServerIP.Location = new Point(100, 20);
this.txtServerIP.Size = new Size(150, 20);
this.txtServerIP.Text = "127.0.0.1";
// 服务器端口标签和文本框
this.label2.Location = new Point(270, 20);
this.label2.Size = new Size(80, 20);
this.label2.Text = "服务器端口:";
this.txtServerPort.Location = new Point(350, 20);
this.txtServerPort.Size = new Size(80, 20);
this.txtServerPort.Text = "8080";
// 客户端IP标签和文本框(只读,显示本地信息)
this.label5.Location = new Point(20, 50);
this.label5.Size = new Size(80, 20);
this.label5.Text = "本地IP:";
this.txtClientIP.Location = new Point(100, 50);
this.txtClientIP.Size = new Size(150, 20);
this.txtClientIP.ReadOnly = true;
// 客户端端口标签和文本框(只读,显示本地信息)
this.label6.Location = new Point(270, 50);
this.label6.Size = new Size(80, 20);
this.label6.Text = "本地端口:";
this.txtClientPort.Location = new Point(350, 50);
this.txtClientPort.Size = new Size(80, 20);
this.txtClientPort.ReadOnly = true;
// 连接按钮
this.btnConnect.Location = new Point(450, 18);
this.btnConnect.Size = new Size(80, 25);
this.btnConnect.Text = "连接";
this.btnConnect.Click += new EventHandler(this.btnConnect_Click);
// 断开按钮
this.btnDisconnect.Location = new Point(540, 18);
this.btnDisconnect.Size = new Size(80, 25);
this.btnDisconnect.Text = "断开";
this.btnDisconnect.Enabled = false;
this.btnDisconnect.Click += new EventHandler(this.btnDisconnect_Click);
// 自动重连复选框
this.chkAutoReconnect.Location = new Point(450, 50);
this.chkAutoReconnect.Size = new Size(120, 20);
this.chkAutoReconnect.Text = "自动重连";
// 状态标签
this.lblStatus.Location = new Point(20, 85);
this.lblStatus.Size = new Size(600, 20);
this.lblStatus.Text = "未连接";
// 接收数据标签和文本框
this.label3.Location = new Point(20, 120);
this.label3.Size = new Size(100, 20);
this.label3.Text = "接收数据:";
this.txtReceive.Location = new Point(20, 140);
this.txtReceive.Multiline = true;
this.txtReceive.ScrollBars = ScrollBars.Vertical;
this.txtReceive.Size = new Size(650, 250);
this.txtReceive.ReadOnly = true;
// 清空按钮
this.btnClear.Location = new Point(580, 115);
this.btnClear.Size = new Size(90, 25);
this.btnClear.Text = "清空接收区";
this.btnClear.Click += new EventHandler(this.btnClear_Click);
// 发送数据标签和文本框
this.label4.Location = new Point(20, 410);
this.label4.Size = new Size(100, 20);
this.label4.Text = "发送数据:";
this.txtSend.Location = new Point(20, 430);
this.txtSend.Multiline = true;
this.txtSend.ScrollBars = ScrollBars.Vertical;
this.txtSend.Size = new Size(560, 80);
// 发送按钮
this.btnSend.Location = new Point(590, 430);
this.btnSend.Size = new Size(80, 80);
this.btnSend.Text = "发送";
this.btnSend.Enabled = false;
this.btnSend.Click += new EventHandler(this.btnSend_Click);
// 添加控件到窗体
this.Controls.AddRange(new Control[] {
this.txtServerIP, this.txtServerPort, this.btnConnect, this.btnDisconnect,
this.txtSend, this.btnSend, this.txtReceive, this.lblStatus,
this.label1, this.label2, this.label3, this.label4,
this.chkAutoReconnect, this.btnClear, this.txtClientIP,
this.txtClientPort, this.label5, this.label6
});
}
}
}
Form1.cs代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
namespace ClientForm
{
public partial class Form1 : Form
{
/*
public Form1()
{
InitializeComponent();
}
*/
private TcpClient tcpClient;
private NetworkStream clientStream;
private Thread receiveThread;
private bool isConnected = false;
private bool autoReconnect = false;
private int reconnectAttempts = 0;
private const int MAX_RECONNECT_ATTEMPTS = 5;
public Form1()
{
InitializeComponent();
UpdateLocalIPInfo();
}
private void UpdateLocalIPInfo()
{
try
{
// 获取本地IP地址
string localIP = GetLocalIPAddress();
txtClientIP.Text = localIP;
}
catch (Exception ex)
{
txtClientIP.Text = "获取失败";
AddLog($"获取本地IP失败: {ex.Message}");
}
}
private string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("未找到IPv4地址");
}
private void btnConnect_Click(object sender, EventArgs e)
{
ConnectToServer();
}
private void ConnectToServer()
{
try
{
// 验证输入
if (!IPAddress.TryParse(txtServerIP.Text, out IPAddress ipAddress))
{
MessageBox.Show("请输入有效的服务器IP地址");
return;
}
if (!int.TryParse(txtServerPort.Text, out int port) || port < 1 || port > 65535)
{
MessageBox.Show("请输入有效的端口号(1-65535)");
return;
}
// 创建TCP客户端并连接
tcpClient = new TcpClient();
tcpClient.BeginConnect(ipAddress, port, new AsyncCallback(ConnectCallback), tcpClient);
lblStatus.Text = "连接中...";
btnConnect.Enabled = false;
}
catch (Exception ex)
{
MessageBox.Show($"连接失败: {ex.Message}");
lblStatus.Text = "连接失败";
btnConnect.Enabled = true;
}
}
private void ConnectCallback(IAsyncResult ar)
{
try
{
TcpClient client = (TcpClient)ar.AsyncState;
client.EndConnect(ar);
// 连接成功
isConnected = true;
clientStream = client.GetStream();
reconnectAttempts = 0;
// 更新本地端口信息
string localPort = ((IPEndPoint)client.Client.LocalEndPoint).Port.ToString();
Invoke(new Action(() =>
{
txtClientPort.Text = localPort;
lblStatus.Text = $"已连接到 {txtServerIP.Text}:{txtServerPort.Text}";
btnConnect.Enabled = false;
btnDisconnect.Enabled = true;
btnSend.Enabled = true;
AddLog($"连接成功 - 本地端口: {localPort}");
}));
// 启动接收线程
receiveThread = new Thread(new ThreadStart(ReceiveData));
receiveThread.IsBackground = true;
receiveThread.Start();
}
catch (Exception ex)
{
Invoke(new Action(() =>
{
lblStatus.Text = "连接失败";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
AddLog($"连接失败: {ex.Message}");
// 自动重连逻辑
if (autoReconnect && reconnectAttempts < MAX_RECONNECT_ATTEMPTS)
{
reconnectAttempts++;
AddLog($"尝试自动重连 ({reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})...");
Thread.Sleep(3000); // 等待3秒后重连
ConnectToServer();
}
}));
}
}
private void ReceiveData()
{
byte[] buffer = new byte[4096];
int bytesRead;
while (isConnected && tcpClient != null && tcpClient.Connected)
{
try
{
bytesRead = clientStream.Read(buffer, 0, buffer.Length);
if (bytesRead == 0)
{
// 连接已关闭
HandleDisconnection("服务器关闭了连接");
break;
}
string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Invoke(new Action(() =>
{
AddLog($"接收: {receivedData}");
}));
}
catch (IOException)
{
// 连接被关闭
HandleDisconnection("连接被中断");
break;
}
catch (ObjectDisposedException)
{
// 对象已被释放
break;
}
catch (Exception ex)
{
if (isConnected)
{
Invoke(new Action(() => AddLog($"接收数据时出错: {ex.Message}")));
}
break;
}
}
}
private void HandleDisconnection(string reason)
{
if (!isConnected) return;
isConnected = false;
Invoke(new Action(() =>
{
lblStatus.Text = "连接已断开";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
btnSend.Enabled = false;
AddLog($"连接断开: {reason}");
// 自动重连逻辑
if (autoReconnect && reconnectAttempts < MAX_RECONNECT_ATTEMPTS)
{
reconnectAttempts++;
AddLog($"尝试自动重连 ({reconnectAttempts}/{MAX_RECONNECT_ATTEMPTS})...");
Thread.Sleep(3000);
ConnectToServer();
}
}));
// 清理资源
try
{
clientStream?.Close();
tcpClient?.Close();
}
catch { }
}
private void btnDisconnect_Click(object sender, EventArgs e)
{
Disconnect();
}
private void Disconnect()
{
try
{
isConnected = false;
autoReconnect = false;
// 停止接收线程
receiveThread?.Abort();
receiveThread = null;
// 关闭连接
clientStream?.Close();
tcpClient?.Close();
Invoke(new Action(() =>
{
lblStatus.Text = "已断开连接";
btnConnect.Enabled = true;
btnDisconnect.Enabled = false;
btnSend.Enabled = false;
txtClientPort.Text = "";
AddLog("主动断开连接");
}));
}
catch (Exception ex)
{
Invoke(new Action(() => AddLog($"断开连接时出错: {ex.Message}")));
}
}
private void btnSend_Click(object sender, EventArgs e)
{
SendData();
}
private void SendData()
{
if (!isConnected || tcpClient == null || !tcpClient.Connected)
{
MessageBox.Show("未连接到服务器");
return;
}
if (string.IsNullOrWhiteSpace(txtSend.Text))
{
MessageBox.Show("请输入要发送的数据");
return;
}
try
{
byte[] data = Encoding.UTF8.GetBytes(txtSend.Text);
clientStream.Write(data, 0, data.Length);
clientStream.Flush();
Invoke(new Action(() =>
{
AddLog($"发送: {txtSend.Text}");
txtSend.Clear();
}));
}
catch (Exception ex)
{
Invoke(new Action(() =>
{
AddLog($"发送失败: {ex.Message}");
HandleDisconnection("发送数据时出错");
}));
}
}
private void btnClear_Click(object sender, EventArgs e)
{
txtReceive.Clear();
}
private void chkAutoReconnect_CheckedChanged(object sender, EventArgs e)
{
autoReconnect = chkAutoReconnect.Checked;
}
private void AddLog(string message)
{
if (txtReceive.InvokeRequired)
{
txtReceive.Invoke(new Action<string>(AddLog), message);
}
else
{
txtReceive.AppendText($"[{DateTime.Now:HH:mm:ss}] {message}\r\n");
txtReceive.ScrollToCaret();
}
}
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
// 支持Ctrl+Enter发送
if (e.KeyChar == '\r' && Control.ModifierKeys == Keys.Control)
{
e.Handled = true;
SendData();
}
// 支持Enter发送(需要设置Multiline为true)
else if (e.KeyChar == '\r' && !txtSend.Multiline)
{
e.Handled = true;
SendData();
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
Disconnect();
base.OnFormClosing(e);
}
}
}
三、效果演示
使用网络调试助手验证,设置好ip和端口号,发送和接收数据,验证通过
