Skip to content

Instantly share code, notes, and snippets.

@sh-akira
Created February 22, 2022 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sh-akira/350b3f1161438c4e0726259b1a8741c4 to your computer and use it in GitHub Desktop.
Save sh-akira/350b3f1161438c4e0726259b1a8741c4 to your computer and use it in GitHub Desktop.
.NET Framework 4.7.2でのTCPサーバーの実装サンプル
// 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);
}
}
}
}
// 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