客户端(TcpClient)与服务器端(TcpListener)
【代码】客户端(TcpClient)与服务器端(TcpListener)
·
效果图
实现具体的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace WinFormsApp1
{
public partial class Form2 : Form
{
private TcpClient client;
private NetworkStream stream;
private CancellationTokenSource cts; // 用于控制接收循环的取消
private TcpListener server;
private TcpClient clientd;
private NetworkStream streamd;
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
private async void button1_Click(object sender, EventArgs e)
{
client = new TcpClient();
string ip = textBox2.Text.Trim().ToString();
int port = Convert.ToInt32(textBox3.Text.Trim().ToString());
await client.ConnectAsync(ip, port);
stream = client.GetStream();
textBoxStatus.AppendText("Connected to server.\r\n");
// 初始化 CancellationTokenSource 并启动接收循环
cts = new CancellationTokenSource();
Task.Run(() => ListenForResponses(cts.Token), cts.Token);
}
private async Task ListenForResponses(CancellationToken token)
{
byte[] buffer = new byte[1024];
int bytesRead;
try
{
while (!token.IsCancellationRequested)
{
if (stream.DataAvailable) // 检查是否有数据可用(可选,但可以提高效率)
{
bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
// 由于我们在非UI线程上,我们需要使用Invoke来更新UI
UpdateStatus($"Received: {response}\r\n");
}
// 如果没有DataAvailable检查,这里可以添加一些延迟以避免忙等待
// 例如:await Task.Delay(100, token);
}
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
// 这是预期的取消,不需要处理异常
}
catch (Exception ex)
{
// 处理其他可能的异常,例如流已关闭
UpdateStatus($"Error: {ex.Message}\r\n");
}
finally
{
// 通常,这里不应该关闭流,因为用户可能希望重新连接
// 但为了完整性,这里保留了这个finally块来演示如何关闭流(尽管这样做可能会导致问题)
// 在实际应用中,您应该在DisconnectButton_Click中处理这些关闭操作
}
}
private void UpdateStatus(string text)
{
// 使用Invoke来确保线程安全地更新UI
if (textBoxStatus.InvokeRequired)
{
textBoxStatus.Invoke(new Action<string>(UpdateStatus), text);
}
else
{
textBoxStatus.AppendText(text);
}
}
private async void button2_Click(object sender, EventArgs e)
{
string message = textBoxMessage.Text;
byte[] data = Encoding.ASCII.GetBytes(message);
Task.Run(async () => await stream.WriteAsync(data, 0, data.Length));
textBoxStatus.AppendText($"Sent: {message}\r\n");
}
private void button3_Click(object sender, EventArgs e)
{
// 取消接收操作
cts?.Cancel();
cts = null; // 重置CancellationTokenSource(如果需要重新连接)
// 关闭流和客户端
stream?.Close();
client?.Close();
textBoxStatus.AppendText("Disconnected from server.\r\n");
}
private async void button4_Click(object sender, EventArgs e)
{
server = new TcpListener(IPAddress.Any, 5000);
server.Start();
textBoxStatusd.AppendText("Server started...\r\n");
TcpClient clientHandler = await server.AcceptTcpClientAsync();
clientd = clientHandler;
streamd = clientd.GetStream();
Task.Run(() => ListenForMessages());
}
private async void ListenForMessages()
{
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = await streamd.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string message = Encoding.ASCII.GetString(buffer, 0, bytesRead);
// 检查是否需要跨线程调用
if (textBoxStatusd.InvokeRequired)
{
// 使用 Invoke 在 UI 线程上执行操作
textBoxStatusd.Invoke(new Action(() =>
{
textBoxStatusd.AppendText($"Received: {message}\r\n");
}));
}
else
{
// 如果已经在 UI 线程上,则直接更新
textBoxStatusd.AppendText($"Received: {message}\r\n");
}
// 发送回应
string response = "Message received"; // 可以根据需要修改回应内容
await SendResponseAsync(response);
}
}
private void button5_Click(object sender, EventArgs e)
{
if (clientd != null)
{
streamd.Close();
clientd.Close();
}
server.Stop();
textBoxStatusd.AppendText("Server stopped.\r\n");
}
private async Task SendResponseAsync(string response)
{
byte[] responseBytes = Encoding.ASCII.GetBytes(response);
await streamd.WriteAsync(responseBytes, 0, responseBytes.Length);
// 使用 Invoke 或 BeginInvoke 来确保在 UI 线程上更新文本框
if (textBoxStatusd.InvokeRequired)
{
textBoxStatusd.Invoke(new Action(() =>
{
textBoxStatusd.AppendText($"Send: {response}\r\n");
}));
}
else
{
textBoxStatusd.AppendText($"Send: {response}\r\n");
}
}
private async void button6_Click(object sender, EventArgs e)
{
// 发送回应
string response = textBox4.Text.Trim().ToString(); // 可以根据需要修改回应内容
await SendResponseAsync(response);
}
}
}
更多推荐
所有评论(0)