我将基于之前的TCP/UDP网络小助手,扩展支持多客户端(TCP模式下服务器处理多个客户端连接)和文件传输功能(支持发送和接收文件)。以下是更新的开发步骤和完整源码,包含多客户端管理和文件传输逻辑。
更新开发步骤 1. 多客户端支持: – 修改TCP服务器,使用`List<TcpClient>`存储所有连接的客户端。 – 为每个客户端开启独立的异步任务处理消息,广播消息给其他客户端。 – 添加客户端断开连接的检测和清理逻辑。
2. 文件传输功能: – 添加文件选择按钮和文件路径显示,使用`OpenFileDialog`选择文件。 – 实现文件传输协议:发送文件前发送文件名和文件大小,接收端解析并保存。 – 支持TCP和UDP文件传输(TCP使用流,UDP分块发送)。
3. 界面更新: – 添加文件选择按钮、文件路径文本框和发送文件按钮。 – 显示文件传输进度和状态。
4. 逻辑优化: – 增加消息类型区分(文本消息和文件传输)。 – 改进异常处理,确保多客户端和文件传输的稳定性。
5. 测试: – 测试多客户端连接和消息广播。 – 测试文件传输(小文件和大文件),验证完整性和正确性。
更新源码 以下是扩展后的C# WinForms程序源码,包含多客户端支持和文件传输功能。
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace NetworkAssistant
{
public partial class Form1 : Form
{
private TcpListener tcpListener;
private TcpClient tcpClient;
private UdpClient udpClient;
private List<TcpClient> tcpClients = new List<TcpClient>();
private bool isServerRunning;
private bool isClientConnected;
private OpenFileDialog openFileDialog;
public Form1()
{
InitializeComponent();
openFileDialog = new OpenFileDialog();
}
// 窗体初始化
private void InitializeComponent()
{
this.comboBoxProtocol = new ComboBox();
this.labelIP = new Label();
this.textBoxIP = new TextBox();
this.labelPort = new Label();
this.textBoxPort = new TextBox();
this.buttonStartServer = new Button();
this.buttonConnectClient = new Button();
this.textBoxSend = new TextBox();
this.buttonSend = new Button();
this.textBoxReceive = new TextBox();
this.buttonClear = new Button();
this.buttonSelectFile = new Button();
this.textBoxFilePath = new TextBox();
this.buttonSendFile = new Button();
// comboBoxProtocol
this.comboBoxProtocol.Items.AddRange(new object[] { "TCP", "UDP" });
this.comboBoxProtocol.Location = new System.Drawing.Point(20, 20);
this.comboBoxProtocol.Size = new System.Drawing.Size(100, 21);
this.comboBoxProtocol.SelectedIndex = 0;
// labelIP
this.labelIP.Text = "IP Address:";
this.labelIP.Location = new System.Drawing.Point(20, 50);
this.labelIP.Size = new System.Drawing.Size(60, 20);
// textBoxIP
this.textBoxIP.Text = "127.0.0.1";
this.textBoxIP.Location = new System.Drawing.Point(90, 50);
this.textBoxIP.Size = new System.Drawing.Size(150, 20);
// labelPort
this.labelPort.Text = "Port:";
this.labelPort.Location = new System.Drawing.Point(20, 80);
this.labelPort.Size = new System.Drawing.Size(60, 20);
// textBoxPort
this.textBoxPort.Text = "8080";
this.textBoxPort.Location = new System.Drawing.Point(90, 80);
this.textBoxPort.Size = new System.Drawing.Size(150, 20);
// buttonStartServer
this.buttonStartServer.Text = "Start Server";
this.buttonStartServer.Location = new System.Drawing.Point(20, 110);
this.buttonStartServer.Size = new System.Drawing.Size(100, 30);
this.buttonStartServer.Click += new EventHandler(this.buttonStartServer_Click);
// buttonConnectClient
this.buttonConnectClient.Text = "Connect Client";
this.buttonConnectClient.Location = new System.Drawing.Point(140, 110);
this.buttonConnectClient.Size = new System.Drawing.Size(100, 30);
this.buttonConnectClient.Click += new EventHandler(this.buttonConnectClient_Click);
// textBoxSend
this.textBoxSend.Location = new System.Drawing.Point(20, 150);
this.textBoxSend.Size = new System.Drawing.Size(300, 50);
this.textBoxSend.Multiline = true;
// buttonSend
this.buttonSend.Text = "Send Text";
this.buttonSend.Location = new System.Drawing.Point(330, 150);
this.buttonSend.Size = new System.Drawing.Size(80, 50);
this.buttonSend.Click += new EventHandler(this.buttonSend_Click);
// buttonSelectFile
this.buttonSelectFile.Text = "Select File";
this.buttonSelectFile.Location = new System.Drawing.Point(20, 210);
this.buttonSelectFile.Size = new System.Drawing.Size(80, 30);
this.buttonSelectFile.Click += new EventHandler(this.buttonSelectFile_Click);
// textBoxFilePath
this.textBoxFilePath.Location = new System.Drawing.Point(110, 210);
this.textBoxFilePath.Size = new System.Drawing.Size(210, 20);
this.textBoxFilePath.ReadOnly = true;
// buttonSendFile
this.buttonSendFile.Text = "Send File";
this.buttonSendFile.Location = new System.Drawing.Point(330, 210);
this.buttonSendFile.Size = new System.Drawing.Size(80, 30);
this.buttonSendFile.Click += new EventHandler(this.buttonSendFile_Click);
// textBoxReceive
this.textBoxReceive.Location = new System.Drawing.Point(20, 250);
this.textBoxReceive.Size = new System.Drawing.Size(390, 150);
this.textBoxReceive.Multiline = true;
this.textBoxReceive.ReadOnly = true;
// buttonClear
this.buttonClear.Text = "Clear";
this.buttonClear.Location = new System.Drawing.Point(20, 410);
this.buttonClear.Size = new System.Drawing.Size(80, 30);
this.buttonClear.Click += new EventHandler(this.buttonClear_Click);
// Form
this.Text = "TCP/UDP Network Assistant";
this.Size = new System.Drawing.Size(450, 480);
this.Controls.Add(this.comboBoxProtocol);
this.Controls.Add(this.labelIP);
this.Controls.Add(this.textBoxIP);
this.Controls.Add(this.labelPort);
this.Controls.Add(this.textBoxPort);
this.Controls.Add(this.buttonStartServer);
this.Controls.Add(this.buttonConnectClient);
this.Controls.Add(this.textBoxSend);
this.Controls.Add(this.buttonSend);
this.Controls.Add(this.buttonSelectFile);
this.Controls.Add(this.textBoxFilePath);
this.Controls.Add(this.buttonSendFile);
this.Controls.Add(this.textBoxReceive);
this.Controls.Add(this.buttonClear);
}
// 启动服务器
private async void buttonStartServer_Click(object sender, EventArgs e)
{
if (isServerRunning)
{
MessageBox.Show("Server is already running!");
return;
}
try
{
string ip = textBoxIP.Text;
int port = int.Parse(textBoxPort.Text);
isServerRunning = true;
buttonStartServer.Enabled = false;
if (comboBoxProtocol.SelectedItem.ToString() == "TCP")
{
await StartTcpServer(ip, port);
}
else
{
await StartUdpServer(ip, port);
}
}
catch (Exception ex)
{
isServerRunning = false;
buttonStartServer.Enabled = true;
MessageBox.Show($"Error starting server: {ex.Message}");
}
}
// TCP服务器(支持多客户端)
private async Task StartTcpServer(string ip, int port)
{
tcpListener = new TcpListener(IPAddress.Parse(ip), port);
tcpListener.Start();
AppendReceiveText("TCP Server started…");
while (isServerRunning)
{
try
{
TcpClient client = await tcpListener.AcceptTcpClientAsync();
lock (tcpClients)
{
tcpClients.Add(client);
}
AppendReceiveText($"Client connected. Total clients: {tcpClients.Count}");
_ = HandleTcpClient(client); // 异步处理客户端
}
catch (Exception ex)
{
AppendReceiveText($"Server error: {ex.Message}");
break;
}
}
}
// 处理TCP客户端
private async Task HandleTcpClient(TcpClient client)
{
try
{
using (NetworkStream stream = client.GetStream())
{
byte[] buffer = new byte[1024];
while (isServerRunning)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
// 解析消息类型
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
if (message.StartsWith("FILE:"))
{
await ReceiveFileTcp(stream, message);
}
else
{
AppendReceiveText($"Received: {message}");
await BroadcastTcpMessage(message, client);
}
}
}
}
catch (Exception ex)
{
AppendReceiveText($"Client error: {ex.Message}");
}
finally
{
lock (tcpClients)
{
tcpClients.Remove(client);
}
client.Close();
AppendReceiveText($"Client disconnected. Total clients: {tcpClients.Count}");
}
}
// 广播消息给所有TCP客户端
private async Task BroadcastTcpMessage(string message, TcpClient sender)
{
byte[] buffer = Encoding.UTF8.GetBytes(message);
lock (tcpClients)
{
foreach (var client in tcpClients)
{
if (client != sender)
{
try
{
NetworkStream stream = client.GetStream();
stream.WriteAsync(buffer, 0, buffer.Length);
}
catch { }
}
}
}
}
// UDP服务器
private async Task StartUdpServer(string ip, int port)
{
udpClient = new UdpClient(port);
AppendReceiveText("UDP Server started…");
while (isServerRunning)
{
try
{
UdpReceiveResult result = await udpClient.ReceiveAsync();
string message = Encoding.UTF8.GetString(result.Buffer);
if (message.StartsWith("FILE:"))
{
await ReceiveFileUdp(result.RemoteEndPoint);
}
else
{
AppendReceiveText($"Received from {result.RemoteEndPoint}: {message}");
}
}
catch (Exception ex)
{
AppendReceiveText($"Server error: {ex.Message}");
break;
}
}
}
// 连接客户端
private async void buttonConnectClient_Click(object sender, EventArgs e)
{
if (isClientConnected)
{
MessageBox.Show("Client is already connected!");
return;
}
try
{
string ip = textBoxIP.Text;
int port = int.Parse(textBoxPort.Text);
isClientConnected = true;
buttonConnectClient.Enabled = false;
if (comboBoxProtocol.SelectedItem.ToString() == "TCP")
{
tcpClient = new TcpClient();
await tcpClient.ConnectAsync(ip, port);
AppendReceiveText("Connected to TCP server.");
_ = ReceiveTcpClientMessages();
}
else
{
udpClient = new UdpClient();
AppendReceiveText("UDP Client ready.");
}
}
catch (Exception ex)
{
isClientConnected = false;
buttonConnectClient.Enabled = true;
MessageBox.Show($"Error connecting: {ex.Message}");
}
}
// 接收TCP客户端消息
private async Task ReceiveTcpClientMessages()
{
try
{
using (NetworkStream stream = tcpClient.GetStream())
{
byte[] buffer = new byte[1024];
while (isClientConnected)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
if (message.StartsWith("FILE:"))
{
await ReceiveFileTcp(stream, message);
}
else
{
AppendReceiveText($"Received: {message}");
}
}
}
}
catch (Exception ex)
{
AppendReceiveText($"Client error: {ex.Message}");
}
finally
{
isClientConnected = false;
buttonConnectClient.Enabled = true;
tcpClient?.Close();
AppendReceiveText("Disconnected from server.");
}
}
// 发送文本消息
private async void buttonSend_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(textBoxSend.Text))
{
MessageBox.Show("Please enter a message to send.");
return;
}
try
{
string message = textBoxSend.Text;
byte[] buffer = Encoding.UTF8.GetBytes(message);
if (comboBoxProtocol.SelectedItem.ToString() == "TCP")
{
if (isClientConnected && tcpClient != null)
{
NetworkStream stream = tcpClient.GetStream();
await stream.WriteAsync(buffer, 0, buffer.Length);
AppendReceiveText($"Sent: {message}");
}
else
{
MessageBox.Show("Client not connected!");
}
}
else
{
if (udpClient != null)
{
string ip = textBoxIP.Text;
int port = int.Parse(textBoxPort.Text);
await udpClient.SendAsync(buffer, buffer.Length, ip, port);
AppendReceiveText($"Sent: {message}");
}
else
{
MessageBox.Show("UDP client not initialized!");
}
}
textBoxSend.Clear();
}
catch (Exception ex)
{
MessageBox.Show($"Error sending message: {ex.Message}");
}
}
// 选择文件
private void buttonSelectFile_Click(object sender, EventArgs e)
{
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
textBoxFilePath.Text = openFileDialog.FileName;
}
}
// 发送文件
private async void buttonSendFile_Click(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(textBoxFilePath.Text) || !File.Exists(textBoxFilePath.Text))
{
MessageBox.Show("Please select a valid file.");
return;
}
try
{
string filePath = textBoxFilePath.Text;
string fileName = Path.GetFileName(filePath);
long fileSize = new FileInfo(filePath).Length;
if (comboBoxProtocol.SelectedItem.ToString() == "TCP")
{
if (isClientConnected && tcpClient != null)
{
await SendFileTcp(filePath, fileName, fileSize);
AppendReceiveText($"Sent file: {fileName}");
}
else
{
MessageBox.Show("Client not connected!");
}
}
else
{
if (udpClient != null)
{
await SendFileUdp(filePath, fileName, fileSize);
AppendReceiveText($"Sent file: {fileName}");
}
else
{
MessageBox.Show("UDP client not initialized!");
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error sending file: {ex.Message}");
}
}
// 发送文件(TCP)
private async Task SendFileTcp(string filePath, string fileName, long fileSize)
{
NetworkStream stream = tcpClient.GetStream();
// 发送文件头
string header = $"FILE:{fileName}:{fileSize}";
byte[] headerBytes = Encoding.UTF8.GetBytes(header);
await stream.WriteAsync(headerBytes, 0, headerBytes.Length);
// 发送文件内容
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await stream.WriteAsync(buffer, 0, bytesRead);
}
}
}
// 接收文件(TCP)
private async Task ReceiveFileTcp(NetworkStream stream, string header)
{
try
{
string[] parts = header.Split(':');
string fileName = parts[1];
long fileSize = long.Parse(parts[2]);
string savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName);
using (FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Write))
{
byte[] buffer = new byte[8192];
long totalBytesRead = 0;
while (totalBytesRead < fileSize)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
await fs.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
}
AppendReceiveText($"Received file: {fileName}, saved to {savePath}");
}
catch (Exception ex)
{
AppendReceiveText($"Error receiving file: {ex.Message}");
}
}
// 发送文件(UDP)
private async Task SendFileUdp(string filePath, string fileName, long fileSize)
{
string ip = textBoxIP.Text;
int port = int.Parse(textBoxPort.Text);
// 发送文件头
string header = $"FILE:{fileName}:{fileSize}";
byte[] headerBytes = Encoding.UTF8.GetBytes(header);
await udpClient.SendAsync(headerBytes, headerBytes.Length, ip, port);
// 发送文件内容
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
await udpClient.SendAsync(buffer, bytesRead, ip, port);
}
}
}
// 接收文件(UDP)
private async Task ReceiveFileUdp(IPEndPoint remoteEndPoint)
{
try
{
UdpReceiveResult headerResult = await udpClient.ReceiveAsync();
string header = Encoding.UTF8.GetString(headerResult.Buffer);
string[] parts = header.Split(':');
string fileName = parts[1];
long fileSize = long.Parse(parts[2]);
string savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName);
using (FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Write))
{
long totalBytesRead = 0;
while (totalBytesRead < fileSize)
{
UdpReceiveResult result = await udpClient.ReceiveAsync();
await fs.WriteAsync(result.Buffer, 0, result.Buffer.Length);
totalBytesRead += result.Buffer.Length;
}
}
AppendReceiveText($"Received file: {fileName}, saved to {savePath}");
}
catch (Exception ex)
{
AppendReceiveText($"Error receiving file: {ex.Message}");
}
}
// 清空接收消息
private void buttonClear_Click(object sender, EventArgs e)
{
textBoxReceive.Clear();
}
// 追加接收消息到文本框
private void AppendReceiveText(string text)
{
if (textBoxReceive.InvokeRequired)
{
textBoxReceive.Invoke(new Action(() => textBoxReceive.AppendText(text + Environment.NewLine)));
}
else
{
textBoxReceive.AppendText(text + Environment.NewLine);
}
}
// 窗体关闭时清理资源
protected override void OnFormClosing(FormClosingEventArgs e)
{
isServerRunning = false;
isClientConnected = false;
tcpListener?.Stop();
lock (tcpClients)
{
foreach (var client in tcpClients)
{
client.Close();
}
tcpClients.Clear();
}
tcpClient?.Close();
udpClient?.Close();
base.OnFormClosing(e);
}
private ComboBox comboBoxProtocol;
private Label labelIP;
private TextBox textBoxIP;
private Label labelPort;
private TextBox textBoxPort;
private Button buttonStartServer;
private Button buttonConnectClient;
private TextBox textBoxSend;
private Button buttonSend;
private TextBox textBoxReceive;
private Button buttonClear;
private Button buttonSelectFile;
private TextBox textBoxFilePath;
private Button buttonSendFile;
}
}
使用说明 1. 运行程序: – 在Visual Studio中创建新WinForms项目,替换`Form1.cs`为上述代码。 – 编译并运行。
2. 操作步骤: – 选择协议:选择`TCP`或`UDP`。 – 输入IP和端口:默认`127.0.0.1:8080`,可修改。 – 启动服务器:点击“Start Server”,TCP模式支持多客户端连接。 – 连接客户端:在多个实例中点击“Connect Client”(TCP需要连接,UDP直接发送)。 – 发送文本:输入文本,点击“Send Text”,TCP广播给所有客户端,UDP发送到指定IP。 – 发送文件:点击“Select File”选择文件,点击“Send File”传输,文件保存到接收端桌面。 – 查看消息:文本和文件传输状态显示在接收文本框,点击“Clear”清空。
3. 注意事项: – TCP多客户端:服务器维护所有客户端连接,消息广播给其他客户端,断开连接自动清理。 – 文件传输:文件保存到桌面,UDP传输可能因丢包导致不完整,建议大文件使用TCP。 – 端口占用:确保端口未被占用,异常情况会显示提示。
测试建议 – 多客户端测试: – 启动一个服务器(TCP模式),运行多个客户端实例连接,发送文本消息,验证广播功能。 – 断开一个客户端,检查服务器是否正确清理并更新客户端计数。 – 文件传输测试: – 测试小文件(如文本文件)和大文件(如图片或视频),验证文件完整性。 – 在TCP和UDP模式下分别测试,比较可靠性(TCP更稳定)。 – 网络测试: – 在不同机器上运行,修改IP为目标服务器IP,测试跨设备通信。 – 异常测试: – 模拟网络中断或端口占用,验证错误提示和程序稳定性。
扩展说明 – 多客户端:TCP服务器使用`List<TcpClient>`管理客户端,线程安全地添加和移除客户端,支持消息广播。 – 文件传输:通过发送文件头(文件名和大小)实现可靠传输,TCP使用流传输,UDP分块发送(可能丢包)。 – 局限性: – UDP文件传输未实现重传机制,大文件可能不完整。 – 未添加进度条显示文件传输进度。 – 进一步改进: – 添加文件传输进度条。 – 实现UDP重传机制。 – 支持文件夹传输或加密传输。
这个版本的网络小助手已支持多客户端和文件传输,适合学习和测试网络通信。如需特定功能扩展,请联系我252439060
评论前必须登录!
注册