You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
6.9 KiB

using STcpHelper.Packet;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace STcpHelper
{
public class STcpServer
{
private CancellationTokenSource _cts;
private List<TcpClient> _clients;
private readonly int HEADER_SIZE = STcpPacketHeader.HEADER_LENGTH;
private readonly int BUFFER_SIZE;
private int _port;
private TcpListener _listener;
public event EventHandler<string> MessageCallback;
public event EventHandler<Exception> ErrorCallback;
public STcpServer(int port, int bufferSize = 1024)
{
_port = port;
BUFFER_SIZE = bufferSize;
_clients = new List<TcpClient>();
}
public async void Start()
{
_listener = new TcpListener(IPAddress.Parse("127.0.0.1"), _port);
_listener.Start();
WriteMessage("Server start...");
_cts = new CancellationTokenSource();
while (true)
{
if (_cts.IsCancellationRequested)
break;
TcpClient client = await _listener.AcceptTcpClientAsync();
_clients.Add(client);
WriteMessage($"Client accepted({client.Client.RemoteEndPoint.ToString()})");
_cts = new CancellationTokenSource();
_ = Task.Run(async () =>
{
using (NetworkStream stream = client.GetStream())
{
byte[] headerBuffer = new byte[HEADER_SIZE];
byte[] dataBuffer = new byte[BUFFER_SIZE];
try
{
while (true)
{
int headerBytesRead = await stream.ReadAsync(headerBuffer, 0, HEADER_SIZE);
if (headerBytesRead != HEADER_SIZE)
{
WriteError(new Exception("Cannot read header"));
return;
}
STcpPacketHeader header = new STcpPacketHeader(headerBuffer);
int dataSize = header.DataLength;
int totalRecv = 0;
while (totalRecv < dataSize)
{
int dataBytesRead = await stream.ReadAsync(dataBuffer, totalRecv, dataSize - totalRecv);
totalRecv += dataBytesRead;
}
WriteMessage($"[{client.Client.RemoteEndPoint.ToString()}] Type: {header.Type.ToString()}");
if (header.Type == PacketType.TEXT)
{
STcpTextPacket packet = new STcpTextPacket(dataBuffer);
// Broadcast
BroadcastMessage(packet.Text, client);
}
else if (header.Type == PacketType.REQ_CLIENT_LIST)
{
STcpClientListResPacket packet = new STcpClientListResPacket(_clients);
// Send
SendPacket(packet, client);
}
else if (header.Type == PacketType.REQ_FILE)
{
STcpFileReqPacket recvPacket = new STcpFileReqPacket(dataBuffer);
STcpFileResPacket packet = new STcpFileResPacket(recvPacket.Path);
// Send async
SendAsyncPacket(packet, client);
}
if (!IsClientConnected(client))
break;
}
}
catch (Exception ex)
{
WriteError(ex);
}
WriteMessage($"Client disconnected({client.Client.RemoteEndPoint.ToString()})");
client.Close();
_clients.Remove(client);
}
}, _cts.Token);
}
}
public void Stop()
{
_cts.Cancel();
}
private byte[] MakeTextPacket(string text)
{
STcpTextPacket packet = new STcpTextPacket(text);
return packet.Serialize();
}
private void SendPacket(ISTcpPacket packet, TcpClient requester)
{
byte[] responseByte = packet.Serialize();
if (!IsClientConnected(requester))
return;
if (responseByte.Length < BUFFER_SIZE)
requester.GetStream().WriteAsync(responseByte, 0, responseByte.Length);
else
{
int totalSend = 0;
while (totalSend < responseByte.Length)
{
int sendCount = responseByte.Length - totalSend < BUFFER_SIZE ? responseByte.Length - totalSend : BUFFER_SIZE;
requester.GetStream().WriteAsync(responseByte, totalSend, sendCount);
totalSend += sendCount;
}
}
}
private async void SendAsyncPacket(ISTcpAsyncPacket packet, TcpClient requester)
{
byte[] responseByte = await packet.Serialize();
if (!IsClientConnected(requester))
return;
requester.GetStream().WriteAsync(responseByte, 0, responseByte.Length);
}
private void BroadcastMessage(string message, TcpClient sender)
{
byte[] resonseByte = MakeTextPacket(message);
foreach (TcpClient client in _clients)
{
if (client != sender && IsClientConnected(client))
client.GetStream().WriteAsync(resonseByte, 0, resonseByte.Length);
}
}
private bool IsClientConnected(TcpClient client)
{
try
{
if (client != null &&
client.Client != null &&
client.Client.Connected)
return true;
}
catch (Exception ex)
{
WriteError(ex);
}
return false;
}
private void WriteMessage(string message)
{
if (this.MessageCallback != null)
this.MessageCallback(this, message);
}
private void WriteError(Exception ex)
{
if (this.ErrorCallback != null)
this.ErrorCallback(this, ex);
}
}
}