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.
216 lines
8.2 KiB
216 lines
8.2 KiB
using SObject;
|
|
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);
|
|
}
|
|
else if (header.Type == PacketType.REQ_ZOO)
|
|
{
|
|
//Zoo zoo = new Zoo();
|
|
//Random rand = new Random();
|
|
//zoo.Dogs = new List<DogFamily>()
|
|
//{
|
|
// new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
// new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)),
|
|
//};
|
|
|
|
STcpZooResPacket packet = new STcpZooResPacket(new Zoo());
|
|
|
|
SendPacket(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);
|
|
}
|
|
}
|
|
} |