visual stdio中用C#,TCP协议写一个简陋的服务器
·
窗体1:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace server
{
public partial class Form1 : Form
{
#region TCP服务全局变量
//监听端口
private const int Port = 8899;
//保存所有在线客户端Socket
private readonly List<Socket> _clientSockets = new List<Socket>();
private Socket? _listenSocket;
//标记服务是否运行
private bool _serverRunning = false;
//异步任务保存,防止GC回收
private Task? _serverTask;
private CancellationTokenSource? _cts;
#endregion
public Form1()
{
InitializeComponent();
// 初始化占位文字灰色
textBox1.Text = "请输入对话内容";
textBox1.ForeColor = Color.Gray;
}
//按钮点击事件:启动/停止服务器
private void button1_Click(object sender, EventArgs e)
{
if (!_serverRunning)
{
//启动服务器
_cts = new CancellationTokenSource();
_serverTask = StartServerAsync(_cts.Token);
button1.Text = "停止服务器";
}
else
{
//关闭服务器
StopServer();
button1.Text = "启动服务器";
AppendLog("服务已经关闭");
}
}
#region TCP核心方法
//启动TCP监听
private async Task StartServerAsync(CancellationToken token)
{
try
{
AppendLog(" ");
AppendLog("====== C# WinForm TCP服务端启动 =====");
AppendLog($"监听端口:{Port}");
//AppendLog("客户端消息会实时显示在此窗口\r\n");
_listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_listenSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
_listenSocket.Listen(100);//最大连接数100
_serverRunning = true;
//启动接受连接循环
_ = AcceptClientLoop(token);
//保持服务运行
while (_serverRunning && !token.IsCancellationRequested)
{
await Task.Delay(100, token);
}
}
catch (OperationCanceledException)
{
AppendLog("服务监听已取消");
}
catch (Exception ex)
{
AppendLog($"服务器异常:{ex.Message}");
_serverRunning = false;
Invoke(new Action(() => { button1.Text = "启动服务器"; }));
}
}
//循环等待新的客户端连接
private async Task AcceptClientLoop(CancellationToken token)
{
while (_serverRunning && !token.IsCancellationRequested)
{
try
{
if (_listenSocket is null)
{
break;
}
Socket client = await _listenSocket.AcceptAsync(token);
lock (_clientSockets)
{
_clientSockets.Add(client);
}
IPEndPoint clientEp = client.RemoteEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.None, 0);
AppendLog($"客户端已连接,在线总数:{_clientSockets.Count}");
//开启异步循环接收客户端消息
_ = ReceiveClientMessageLoop(client, token);
}
catch (OperationCanceledException)
{
break;
}
catch (Exception ex)
{
AppendLog($"等待客户端连接出错:{ex.Message}");
}
}
}
//单个客户端循环接收消息
private async Task ReceiveClientMessageLoop(Socket client, CancellationToken token)
{
byte[] buffer = new byte[1024 * 4];
IPEndPoint ep = client.RemoteEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.None, 0);
try
{
while (true && !token.IsCancellationRequested)
{
int len = await client.ReceiveAsync(buffer, SocketFlags.None, token);
if (len <= 0)
{
//客户端断开连接
break;
}
string msg = Encoding.UTF8.GetString(buffer, 0, len);
AppendLog(msg);
//收到消息自动广播给所有客户端
await BroadcastMsgAsync(msg);
}
}
catch (OperationCanceledException)
{
}
catch
{
//客户端异常断开
}
finally
{
//移除离线客户端
lock (_clientSockets)
{
_clientSockets.Remove(client);
}
SafeCloseSocket(client);
AppendLog($"客户端已断开,在线总数:{_clientSockets.Count}");
}
}
//异步广播消息给所有客户端
private async Task BroadcastMsgAsync(string msg)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
List<Socket> tempList;
lock (_clientSockets)
{
tempList = new List<Socket>(_clientSockets);
}
foreach (Socket sock in tempList)
{
try
{
await sock.SendAsync(data, SocketFlags.None);
}
catch (Exception ex)
{
AppendLog($"向客户端发送消息失败:{ex.Message}");
}
}
}
//安全关闭单个Socket
private void SafeCloseSocket(Socket sock)
{
if (sock == null) return;
try
{
sock.Shutdown(SocketShutdown.Both);
}
catch { }
sock.Close();
}
//停止服务、释放全部资源
private void StopServer()
{
_serverRunning = false;
_cts?.Cancel();
//关闭监听Socket
if (_listenSocket != null)
{
SafeCloseSocket(_listenSocket);
_listenSocket = null;
}
//关闭所有客户端连接
lock (_clientSockets)
{
foreach (var sock in _clientSockets)
{
SafeCloseSocket(sock);
}
_clientSockets.Clear();
}
}
#endregion
#region 服务端发送按钮逻辑 button2
private async void button2_Click(object sender, EventArgs e)
{
// 获取底部输入框内容
string sendContent = textBox1.Text.Trim();
// 拦截占位文字、空内容
if (string.IsNullOrWhiteSpace(sendContent) || sendContent == "请输入对话内容")
{
AppendLog("【提示】输入框不能为空或占位提示文字!");
return;
}
if (!_serverRunning)
{
AppendLog("【错误】服务器未启动,无法发送消息");
return;
}
// 拼接服务端标识消息
string sendMsg = $"【服务端】:{sendContent}";
// 本地txtLog打印自己发送的消息
AppendLog(sendMsg);
// 广播给所有客户端
await BroadcastMsgAsync(sendMsg);
// 清空输入框,恢复占位文字
textBox1.Clear();
textBox1.Text = "请输入对话内容";
textBox1.ForeColor = Color.Gray;
}
//可选:输入框回车快捷发送(绑定textBox1 KeyDown事件)
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
button2.PerformClick();
e.SuppressKeyPress = true; //取消回车换行
}
}
#endregion
#region 辅助:跨线程添加日志到文本框
private void AppendLog(string text)
{
if (txtLog.InvokeRequired)
{
txtLog.Invoke(new Action(() =>
{
string logLine = $"{DateTime.Now:HH:mm:ss} {text}\r\n";
txtLog.AppendText(logLine);
txtLog.ScrollToCaret();
}));
return;
}
string line = $"{DateTime.Now:HH:mm:ss} {text}\r\n";
txtLog.AppendText(line);
txtLog.ScrollToCaret();
}
#endregion
//窗体关闭时强制停止服务,释放资源
protected override void OnFormClosed(FormClosedEventArgs e)
{
StopServer();
base.OnFormClosed(e);
}
// 输入框获得焦点,清空占位文字
private void textBox1_Enter(object sender, EventArgs e)
{
if (textBox1.Text == "请输入对话内容")
{
textBox1.Clear();
// 文字颜色改为黑色
textBox1.ForeColor = Color.Black;
}
}
// 输入框失去焦点,无内容则恢复占位提示
private void textBox1_Leave(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(textBox1.Text))
{
textBox1.Text = "请输入对话内容";
// 占位文字灰色区分
textBox1.ForeColor = Color.Gray;
}
}
private void button3_Click(object sender, EventArgs e)
{
Form2 client = new Form2();
client.Show();
}
//private void textBox1_KeyDown1(object sender, KeyEventArgs e)
//{
// if (e.KeyCode == Keys.Enter)
// {
// // 禁止占位文字发送
// if (textBox1.Text == "请输入对话内容")
// return;
// button2.PerformClick();
// e.SuppressKeyPress = true; //取消回车换行
// }
//}
}
}
窗体2:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace server
{
public partial class Form2 : Form
{
// 客户端全局变量
private Socket? _clientSocket;
private bool _isConnected = false;
private CancellationTokenSource? _cts;
public Form2()
{
InitializeComponent();
// 默认填充本地测试地址
txtIP.Text = "127.0.0.1";
txtPort.Text = "8899";
txtInput.Text = "请输入对话内容";
txtInput.ForeColor = Color.Gray;
btnSend.Enabled = false; // 未连接时发送按钮不可用
}
#region 连接/断开按钮事件
private async void btnConnect_Click(object sender, EventArgs e)
{
if (!_isConnected)
{
//执行连接
await ConnectServer();
}
else
{
//断开连接
DisconnectServer();
}
}
// 连接服务端
private async Task ConnectServer()
{
try
{
// 校验IP和端口
if (string.IsNullOrWhiteSpace(txtIP.Text) || string.IsNullOrWhiteSpace(txtPort.Text))
{
AppendLog("【错误】IP或端口不能为空!");
return;
}
if (!int.TryParse(txtPort.Text, out int port))
{
AppendLog("【错误】端口号必须是数字!");
return;
}
IPAddress ip = IPAddress.Parse(txtIP.Text.Trim());
IPEndPoint endPoint = new IPEndPoint(ip, port);
// 创建Socket
_clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
AppendLog("正在连接服务端...");
await _clientSocket.ConnectAsync(endPoint);
// 连接成功
_isConnected = true;
btnConnect.Text = "断开";
btnSend.Enabled = true;
AppendLog($"【成功】已连接服务端 {txtIP.Text}:{port}");
// 启动异步接收消息循环
_cts = new CancellationTokenSource();
_ = ReceiveMessageLoop(_cts.Token);
}
catch (Exception ex)
{
AppendLog($"【连接失败】{ex.Message}");
SafeCloseSocket();
}
}
/// 断开服务端连接,释放资源
private void DisconnectServer()
{
_isConnected = false;
btnConnect.Text = "连接/断开";
btnSend.Enabled = false;
_cts?.Cancel();
SafeCloseSocket();
AppendLog("已断开与服务端的连接");
}
/// 安全释放Socket
private void SafeCloseSocket()
{
if (_clientSocket == null) return;
try
{
_clientSocket.Shutdown(SocketShutdown.Both);
}
catch { }
_clientSocket.Close();
_clientSocket = null;
}
#endregion
#region 持续接收服务端消息循环
private async Task ReceiveMessageLoop(CancellationToken token)
{
byte[] buffer = new byte[1024 * 4];
try
{
while (_isConnected && !token.IsCancellationRequested)
{
// 新增空校验,消除警告
if (_clientSocket is null)
break;
int len = await _clientSocket.ReceiveAsync(buffer, SocketFlags.None, token);
if (len <= 0)
{
// 服务端主动断开
AppendLog("【提示】服务端已断开连接");
break;
}
string msg = Encoding.UTF8.GetString(buffer, 0, len);
AppendLog(msg);
}
}
catch (OperationCanceledException)
{
AppendLog("【接收】监听已取消");
}
catch (Exception ex)
{
AppendLog($"【接收消息异常】{ex.Message}");
}
finally
{
// 自动断开
Invoke(new Action(DisconnectServer));
}
}
#endregion
#region 发送消息按钮
private async void btnSend_Click(object sender, EventArgs e)
{
string content = txtInput.Text.Trim();
// 过滤占位文字、空内容
if (string.IsNullOrWhiteSpace(content) || content == "请输入对话内容")
{
AppendLog("【提示】输入框不能为空!");
return;
}
if (!_isConnected || _clientSocket == null)
{
AppendLog("【错误】未连接服务端,无法发送");
return;
}
try
{
// 拼接客户端标识
string sendMsg = $"【客户端】:{content}";
byte[] data = Encoding.UTF8.GetBytes(sendMsg);
await _clientSocket.SendAsync(data, SocketFlags.None);
// 清空输入框,恢复占位
txtInput.Clear();
txtInput.Text = "请输入对话内容";
txtInput.ForeColor = Color.Gray;
}
catch (Exception ex)
{
AppendLog($"【发送失败】{ex.Message}");
DisconnectServer();
}
}
#endregion
#region 输入框占位文字逻辑(焦点事件)
private void txtInput_Enter(object sender, EventArgs e)
{
if (txtInput.Text == "请输入对话内容")
{
txtInput.Clear();
txtInput.ForeColor = Color.Black;
}
}
private void txtInput_Leave(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(txtInput.Text))
{
txtInput.Text = "请输入对话内容";
txtInput.ForeColor = Color.Gray;
}
}
// 回车快捷发送
private void txtInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
btnSend.PerformClick();
e.SuppressKeyPress = true;
}
}
#endregion
#region 日志输出(跨线程安全)
private void AppendLog(string text)
{
if (txtLog.InvokeRequired)
{
txtLog.Invoke(new Action(() =>
{
string line = $"[{DateTime.Now:HH:mm:ss}] {text}\r\n";
txtLog.AppendText(line);
txtLog.ScrollToCaret();
}));
return;
}
string logLine = $"[{DateTime.Now:HH:mm:ss}] {text}\r\n";
txtLog.AppendText(logLine);
txtLog.ScrollToCaret();
}
#endregion
#region 窗体关闭自动断开
private void FormClient_FormClosed(object sender, FormClosedEventArgs e)
{
DisconnectServer();
}
#endregion
}
}


更多推荐

所有评论(0)