Created
February 22, 2022 14:38
-
-
Save sh-akira/350b3f1161438c4e0726259b1a8741c4 to your computer and use it in GitHub Desktop.
.NET Framework 4.7.2でのTCPサーバーの実装サンプル
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// Copyright (c) 2022 sh_akira | |
using System; | |
using System.Collections.Concurrent; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace akr.Network | |
{ | |
public class ClientObject | |
{ | |
//受信バッファサイズ | |
public const int BufferSize = 1024; | |
//クライアントごとの受信バッファ | |
public byte[] buffer = new byte[BufferSize]; | |
//クライアントのソケット | |
public Socket workSocket = null; | |
} | |
public class AsyncTcpListener | |
{ | |
public ManualResetEvent threadWaiter = new ManualResetEvent(false); | |
public ConcurrentQueue<ClientObject> ClientQueue = new ConcurrentQueue<ClientObject>(); | |
public Socket Listener; | |
public Action<ClientObject, byte[]> DataReceived; | |
public void StartListening(int port) | |
{ | |
var localEndPoint = new IPEndPoint(IPAddress.Any, port); | |
StopListening(); | |
Listener = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); | |
var t = Task.Run(() => | |
{ | |
try | |
{ | |
Listener.Bind(localEndPoint); | |
Listener.Listen(100); //backlogはacceptされてないclientのキューの数 | |
while (Listener != null) | |
{ | |
threadWaiter.Reset(); | |
Console.WriteLine("Waiting for a connection..."); | |
Listener.BeginAccept( | |
new AsyncCallback(AcceptCallback), | |
Listener); | |
// Accept完了まで待つ | |
threadWaiter.WaitOne(); | |
} | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(e.ToString()); | |
} | |
}); | |
} | |
public void StopListening() | |
{ | |
if (Listener != null) | |
{ | |
Listener.Close(); | |
Listener = null; | |
threadWaiter.Set(); | |
} | |
} | |
private void AcceptCallback(IAsyncResult ar) | |
{ | |
threadWaiter.Set(); | |
try | |
{ | |
Socket listener = (Socket)ar.AsyncState; | |
Socket handler = listener.EndAccept(ar); | |
// 新しいクライアントを受け入れ | |
ClientObject state = new ClientObject(); | |
state.workSocket = handler; | |
ClientQueue.Enqueue(state); | |
handler.BeginReceive(state.buffer, 0, ClientObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); | |
} | |
catch (ObjectDisposedException) | |
{ | |
return; | |
} | |
} | |
private void ReadCallback(IAsyncResult ar) | |
{ | |
ClientObject state = (ClientObject)ar.AsyncState; | |
Socket handler = state.workSocket; | |
// クライアントからデータ受信 | |
int bytesRead = handler.EndReceive(ar); | |
if (bytesRead > 0) | |
{ | |
var data = new byte[bytesRead]; | |
Buffer.BlockCopy(state.buffer, 0, data, 0, bytesRead); | |
DataReceived?.Invoke(state, data); | |
handler.BeginReceive(state.buffer, 0, ClientObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); | |
} | |
} | |
public void Send(ClientObject client, byte[] data) | |
{ | |
var handler = client.workSocket; | |
handler.BeginSend(data, 0, data.Length, 0, | |
new AsyncCallback(SendCallback), handler); | |
} | |
public void SendAll(byte[] data) | |
{ | |
while (ClientQueue.TryDequeue(out var client)) | |
{ | |
Send(client, data); | |
} | |
} | |
private void SendCallback(IAsyncResult ar) | |
{ | |
try | |
{ | |
Socket handler = (Socket)ar.AsyncState; | |
int bytesSent = handler.EndSend(ar); | |
Console.WriteLine("Sent {0} bytes to client.", bytesSent); | |
} | |
catch (Exception e) | |
{ | |
Console.WriteLine(e.ToString()); | |
} | |
} | |
public void Disconnect(ClientObject client) | |
{ | |
var handler = client.workSocket; | |
handler.Shutdown(SocketShutdown.Both); | |
handler.Close(); | |
} | |
public void DisconnectAll() | |
{ | |
while (ClientQueue.TryDequeue(out var client)) | |
{ | |
Disconnect(client); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT License | |
// Copyright (c) 2022 sh_akira | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
namespace akr.Network.Sample | |
{ | |
public class SampleReceiver | |
{ | |
private Encoding encoding = Encoding.GetEncoding("Shift_JIS"); | |
private AsyncTcpListener listener = null; | |
private Dictionary<ClientObject, List<byte>> BufferDictionary = new Dictionary<ClientObject, List<byte>>(); | |
public delegate void TestCommandReceivedEventHandler(string message); | |
public event TestCommandReceivedEventHandler TestCommandReceived; | |
public void Start(int port = 39639) | |
{ | |
Stop(); | |
listener = new AsyncTcpListener(); | |
listener.DataReceived += Listener_DataReceived; | |
listener.StartListening(port); | |
} | |
private void Listener_DataReceived(ClientObject client, byte[] data) | |
{ | |
if (BufferDictionary.ContainsKey(client) == false) | |
{ | |
BufferDictionary[client] = new List<byte>(); | |
} | |
var buffer = BufferDictionary[client]; | |
buffer.AddRange(data); | |
//データを受信したらパースする | |
while (buffer.Count > 3) | |
{ | |
//コマンド形式 | |
//[LENGTH(4byte)][COMMAND(1byte)][body(?bytes)] | |
//int,byte,bytes | |
int length = BitConverter.ToInt32(buffer.Take(4).ToArray(), 0); | |
if (buffer.Count < length + 4) break; //長さが足りない時は抜けて次の受信を待つ | |
byte command = buffer[1]; | |
if (command == (byte)'T') | |
{ | |
//Tコマンド | |
//[LENGTH(4byte)][COMMAND(1byte)][MESSAGE(?byte)] | |
//int,byte,string(shift-jis) | |
string message = encoding.GetString(buffer.ToArray(), 5, length - 1); | |
TestCommandReceived?.Invoke(message); | |
} | |
else | |
{ | |
Console.WriteLine("Unknown Command"); | |
} | |
//読み取ったデータはバッファから削除 | |
buffer.RemoveRange(0, length + 1); | |
} | |
} | |
public void Stop() | |
{ | |
listener?.StopListening(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment