|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
using System.Windows.Data;
|
|
|
|
|
using System.Windows.Documents;
|
|
|
|
|
using System.Windows.Input;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
using System.Windows.Media.Imaging;
|
|
|
|
|
using System.Windows.Navigation;
|
|
|
|
|
using System.Windows.Shapes;
|
|
|
|
|
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Http;
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
|
|
|
|
namespace SocketStudy.ServerApp
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Interaction logic for MainWindow.xaml
|
|
|
|
|
/// </summary>
|
|
|
|
|
public partial class MainWindow : Window
|
|
|
|
|
{
|
|
|
|
|
//服务监听
|
|
|
|
|
private TcpListener _tcpServer = null;
|
|
|
|
|
|
|
|
|
|
//存放活动客户端连接字典
|
|
|
|
|
private ConcurrentDictionary<string, TcpClient> _remoteClientDic = new ConcurrentDictionary<string, TcpClient>();
|
|
|
|
|
|
|
|
|
|
//监听线程
|
|
|
|
|
private Thread _listenerThread;
|
|
|
|
|
|
|
|
|
|
public MainWindow()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 窗口呈现完毕事件
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void Window_ContentRendered(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
//初始化客户端
|
|
|
|
|
comboBox_Clients.Items.Clear();
|
|
|
|
|
comboBox_Clients.Items.Insert(insertIndex: 0,"广播");
|
|
|
|
|
comboBox_Clients.SelectedIndex = 0;
|
|
|
|
|
|
|
|
|
|
//默认服务器网址
|
|
|
|
|
comboBox_ServerAddress.Items.Insert(0, "本机所有IP4");
|
|
|
|
|
comboBox_ServerAddress.SelectedIndex = 0;
|
|
|
|
|
|
|
|
|
|
var ip4Addresses = Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork) ?? new IPAddress[] { IPAddress.Any};
|
|
|
|
|
foreach (var ip in ip4Addresses)
|
|
|
|
|
{
|
|
|
|
|
comboBox_ServerAddress.Items.Add(ip);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 启动服务
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void btn_Start_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (_tcpServer?.Server?.IsBound ?? false)
|
|
|
|
|
{
|
|
|
|
|
WriteLog("服务正在运行,无需启动!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IPAddress serverAddress = IPAddress.Any;
|
|
|
|
|
|
|
|
|
|
if (comboBox_ServerAddress.SelectedValue.ToString() != "本机所有IP4")
|
|
|
|
|
{
|
|
|
|
|
serverAddress = (IPAddress)comboBox_ServerAddress.SelectedValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int serverPort = 6605;
|
|
|
|
|
if (!int.TryParse(textBox_ServerPort.Text.Trim(), out serverPort))
|
|
|
|
|
{
|
|
|
|
|
WriteLog("端口号错误");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (serverPort <= 1024 || serverPort >= 65535)
|
|
|
|
|
{
|
|
|
|
|
WriteLog("端口号错误:必须在 1205-65534");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteLog("启动服务...");
|
|
|
|
|
_tcpServer = new TcpListener(serverAddress, serverPort);
|
|
|
|
|
_tcpServer.Start();
|
|
|
|
|
|
|
|
|
|
WriteLog("服务成功启动!");
|
|
|
|
|
|
|
|
|
|
//开启监听线程
|
|
|
|
|
_listenerThread = new Thread(AcceptTcpClient);
|
|
|
|
|
_listenerThread.IsBackground = true;
|
|
|
|
|
_listenerThread.Start(_tcpServer);
|
|
|
|
|
|
|
|
|
|
//控件操作
|
|
|
|
|
comboBox_ServerAddress.IsEnabled = false;
|
|
|
|
|
textBox_ServerPort.IsEnabled = false;
|
|
|
|
|
|
|
|
|
|
WriteLog("服务启动完毕!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 停止服务
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void btn_Stop_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (_tcpServer == null || _tcpServer.Server.IsBound == false)
|
|
|
|
|
{
|
|
|
|
|
WriteLog("服务正未运行,无法停止!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//关闭客户端
|
|
|
|
|
foreach (var item in _remoteClientDic)
|
|
|
|
|
{
|
|
|
|
|
if (item.Value.Connected)
|
|
|
|
|
{
|
|
|
|
|
item.Value.Close();
|
|
|
|
|
item.Value.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteLog("停止服务...");
|
|
|
|
|
_tcpServer?.Stop();
|
|
|
|
|
|
|
|
|
|
//清空客户端
|
|
|
|
|
_remoteClientDic.Clear();
|
|
|
|
|
ResetClientsItems();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//恢复控制状态
|
|
|
|
|
comboBox_ServerAddress.IsEnabled = true;
|
|
|
|
|
textBox_ServerPort.IsEnabled = true;
|
|
|
|
|
|
|
|
|
|
WriteLog("服务停止完成!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 发送数据
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void btn_Send_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (_remoteClientDic.Count == 0 || comboBox_Clients.Items.Count ==0)
|
|
|
|
|
{
|
|
|
|
|
WriteLog("没有正在连接的客户端,请先使用客户端连接到本服务器!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var clientForSend=new List<TcpClient>();
|
|
|
|
|
|
|
|
|
|
var selectedItemValue = comboBox_Clients.SelectedValue as string;
|
|
|
|
|
if (selectedItemValue == "广播")
|
|
|
|
|
{
|
|
|
|
|
clientForSend = _remoteClientDic.Where(q => q.Value.Connected == true).Select(d => d.Value).ToList();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var queryItem = _remoteClientDic.FirstOrDefault(q => q.Key == comboBox_Clients.SelectedValue.ToString());
|
|
|
|
|
|
|
|
|
|
clientForSend.Add(queryItem.Value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var client in clientForSend)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var message = textBox_SendData.Text.Trim();
|
|
|
|
|
byte[] buffer = Encoding.UTF8.GetBytes(message);
|
|
|
|
|
NetworkStream networkStream = client.GetStream();
|
|
|
|
|
networkStream.Write(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
WriteLog($"发送数据成功:{message}");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
WriteLog($"{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 写日志信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void WriteLog(string message)
|
|
|
|
|
{
|
|
|
|
|
Dispatcher.BeginInvoke(new Action(() =>
|
|
|
|
|
{
|
|
|
|
|
richTextBox_Log.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} {message}{System.Environment.NewLine}");
|
|
|
|
|
richTextBox_Log.ScrollToEnd();
|
|
|
|
|
Console.WriteLine(message);
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 重新设置客户端列表项
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ResetClientsItems()
|
|
|
|
|
{
|
|
|
|
|
Dispatcher.BeginInvoke(new Action(() =>
|
|
|
|
|
{
|
|
|
|
|
//字典中的远程客户端同步到下拉列表项
|
|
|
|
|
foreach (var clientItem in _remoteClientDic)
|
|
|
|
|
{
|
|
|
|
|
if (!comboBox_Clients.Items.Contains(clientItem.Key))
|
|
|
|
|
{
|
|
|
|
|
comboBox_Clients.Items.Add(clientItem.Key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//从下拉项中移除已经下线的远程客户端
|
|
|
|
|
for (int i = 0; i < comboBox_Clients.Items.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var itemValue = comboBox_Clients.Items[i] as string;
|
|
|
|
|
|
|
|
|
|
if (itemValue == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (itemValue.Contains("广播"))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_remoteClientDic.ContainsKey(itemValue))
|
|
|
|
|
{
|
|
|
|
|
comboBox_Clients.Items.Remove(comboBox_Clients.Items[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 监听客户端连接方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void AcceptTcpClient(Object? tcpListener)
|
|
|
|
|
{
|
|
|
|
|
TcpListener? tcpServer = tcpListener as TcpListener;
|
|
|
|
|
|
|
|
|
|
if (tcpServer == null)
|
|
|
|
|
{
|
|
|
|
|
WriteLog("调用监听连接方法时,参数为null,将监听不到客户端连接!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WriteLog("开始监听客户端连接...");
|
|
|
|
|
//无限循环:处理多个客户端连接
|
|
|
|
|
while (_tcpServer.Server.IsBound)
|
|
|
|
|
{
|
|
|
|
|
TcpClient remoteClientSocket = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//阻塞等待操作
|
|
|
|
|
remoteClientSocket = _tcpServer.AcceptTcpClient();
|
|
|
|
|
WriteLog("接收到连接请求...");
|
|
|
|
|
|
|
|
|
|
var remoteEndPoint = remoteClientSocket.Client.RemoteEndPoint as IPEndPoint;
|
|
|
|
|
if (remoteEndPoint == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string ipAndPortKey = $"{remoteEndPoint.Address}:{remoteEndPoint.Port}";
|
|
|
|
|
|
|
|
|
|
if (!_remoteClientDic.ContainsKey(ipAndPortKey))
|
|
|
|
|
{
|
|
|
|
|
_remoteClientDic.TryAdd(ipAndPortKey, remoteClientSocket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ResetClientsItems();
|
|
|
|
|
|
|
|
|
|
//接收数据线程
|
|
|
|
|
Thread receviceThread = new Thread(ReciveData);
|
|
|
|
|
receviceThread.IsBackground = true;
|
|
|
|
|
receviceThread.Start(remoteClientSocket);
|
|
|
|
|
|
|
|
|
|
WriteLog("连接请求处理完成!");
|
|
|
|
|
}
|
|
|
|
|
catch (SocketException socketException)
|
|
|
|
|
{
|
|
|
|
|
//移除客户端
|
|
|
|
|
remoteClientSocket?.Close();
|
|
|
|
|
remoteClientSocket?.Dispose();
|
|
|
|
|
|
|
|
|
|
WriteLog($"出现Socket异常:{socketException.Message}");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//移除客户端
|
|
|
|
|
remoteClientSocket?.Close();
|
|
|
|
|
remoteClientSocket?.Dispose();
|
|
|
|
|
|
|
|
|
|
WriteLog($"出现异常:{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//清空客户端
|
|
|
|
|
_remoteClientDic.Clear();
|
|
|
|
|
ResetClientsItems();
|
|
|
|
|
|
|
|
|
|
WriteLog($"监听客户端连接的线程,退出!线程托管Id={Thread.CurrentThread.ManagedThreadId}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 接收客户端数据方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ReciveData(object? tcpClient)
|
|
|
|
|
{
|
|
|
|
|
if (tcpClient == null)
|
|
|
|
|
{
|
|
|
|
|
WriteLog($"接收数据出错:客户端参数{nameof(tcpClient)}为 null");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TcpClient remoteClient = (TcpClient)tcpClient;
|
|
|
|
|
IPEndPoint? remoteEndPoint = remoteClient.Client?.RemoteEndPoint as IPEndPoint;
|
|
|
|
|
string remoteClientKey = $"{remoteEndPoint?.Address}:{remoteEndPoint?.Port}";
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
while (remoteClient.Connected)
|
|
|
|
|
{
|
|
|
|
|
NetworkStream remoteClientStream = remoteClient.GetStream();
|
|
|
|
|
byte[] buffer = new byte[8096];
|
|
|
|
|
|
|
|
|
|
//阻塞等待操作
|
|
|
|
|
var readCount = remoteClientStream.Read(buffer, 0, buffer.Length);
|
|
|
|
|
|
|
|
|
|
//为0代表客户端主动发送关闭操作数据。平常时,即使发送0长度数据也不会是0
|
|
|
|
|
if (readCount == 0)
|
|
|
|
|
{
|
|
|
|
|
remoteClientStream.Close();
|
|
|
|
|
remoteClientStream.Dispose();
|
|
|
|
|
|
|
|
|
|
WriteLog($"接收[{remoteClientKey}]关闭连接请求数据。");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
var message = Encoding.UTF8.GetString(buffer, 0, readCount);
|
|
|
|
|
WriteLog($"接收到[{remoteClientKey}]数据:{message}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (SocketException socketException)
|
|
|
|
|
{
|
|
|
|
|
WriteLog($"接收到[{remoteClientKey}]数据异常:{socketException.Message}");
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
WriteLog($"接收到[{remoteClientKey}]数据异常:{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
remoteClient.Close();
|
|
|
|
|
remoteClient.Dispose();
|
|
|
|
|
|
|
|
|
|
_remoteClientDic.Remove(remoteClientKey, out var sss);
|
|
|
|
|
ResetClientsItems();
|
|
|
|
|
|
|
|
|
|
WriteLog($"接收[{remoteClientKey}]数据的线程退出:线程托管Id={Thread.CurrentThread.ManagedThreadId}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|