Skip to content

Instantly share code, notes, and snippets.

@ichiroku11
Last active March 2, 2021 05:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ichiroku11/1c70dc3d2766e45af665a8e829910800 to your computer and use it in GitHub Desktop.
Save ichiroku11/1c70dc3d2766e45af665a8e829910800 to your computer and use it in GitHub Desktop.
.NETでTCP(TcpClientとTcpListener)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
// クライアント
public class Client<TRequest, TResponse> {
// 接続先のエンドポイント
private readonly IPEndPoint _endpoint;
public Client(IPEndPoint endpoint) {
_endpoint = endpoint;
}
// サーバにリクエストを送信してレスポンスを受信する
public async Task<TResponse> Send(TRequest request) {
using (var client = new TcpClient()) {
// 1. サーバに接続
await client.ConnectAsync(_endpoint.Address, _endpoint.Port);
using (var stream = client.GetStream()) {
// 2. サーバにリクエストを送信する
Console.WriteLine($"Client send: {request}");
stream.WriteObject(request);
// 3. サーバからレスポンスを受信する
var response = stream.ReadObject<TResponse>();
Console.WriteLine($"Client received: {response}");
return response;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
// メッセージ
[Serializable]
public class Message {
public int Id { get; set; }
public string Content { get; set; }
public override string ToString() {
return $@"{{ {nameof(Id)} = {Id}, {nameof(Content)} = ""{Content}"" }}";
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
// オブジェクトとバイト配列の変換
public class ObjectConverter<TObject> {
private readonly IFormatter _formatter;
public ObjectConverter(IFormatter formatter = null) {
_formatter = formatter ?? new BinaryFormatter();
}
// オブジェクト=>バイト配列
public byte[] ToByteArray(TObject obj) {
using (var stream = new MemoryStream()) {
_formatter.Serialize(stream, obj);
return stream.ToArray();
}
}
// バイト配列=>オブジェクト
public TObject FromByteArray(byte[] bytes) {
using (var stream = new MemoryStream(bytes)) {
return (TObject)_formatter.Deserialize(stream);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
class Program {
static void Main(string[] args) {
// サーバが接続を待つエンドポイント
// であり
// クライアントが接続するサーバのエンドポイント
var endpoint = new IPEndPoint(IPAddress.Loopback, 54321);
// サーバ
var server = new Server<Message, Message>(
endpoint,
// リクエストからレスポンスを作る処理
request => new Message {
Id = request.Id,
// メッセージの文字列を逆順にする
Content = new string(request.Content.Reverse().ToArray()),
});
// 接続を待機
var task = Task.Run(() => server.Listen());
// クライアント
Task.WaitAll(
// リクエストを送信してレスポンスを受信
new Client<Message, Message>(endpoint).Send(new Message { Id = 10, Content = "あいうえお" }),
new Client<Message, Message>(endpoint).Send(new Message { Id = 20, Content = "かきくけこ" }),
new Client<Message, Message>(endpoint).Send(new Message { Id = 30, Content = "さしすせそ" })
);
// サーバを終了
server.Close();
// サーバの終了処理、Taskの管理、エラー処理あたりが微妙
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
// サーバ
public class Server<TRequest, TResponse> {
// 接続を待つエンドポイント
private readonly IPEndPoint _endpoint;
// リクエストからレスポンスを作成する処理
private readonly Func<TRequest, TResponse> _processor;
// TCPリスナー
private readonly TcpListener _listener;
public Server(IPEndPoint endpoint, Func<TRequest, TResponse> processor) {
_endpoint = endpoint;
_processor = processor;
_listener = new TcpListener(_endpoint);
}
// クライアントからリクエストを受信してレスポンスを送信する
private void Receive(TcpClient client) {
using (client)
using (var stream = client.GetStream()) {
// 3. クライアントからリクエストを受信する
var request = stream.ReadObject<TRequest>();
// 4. リクエストを処理してレスポンスを作る
var response = _processor(request);
// 5. クライアントにレスポンスを送信する
stream.WriteObject(response);
}
}
// 接続を待つ
public async Task Listen() {
Console.WriteLine($"Server listen:");
// 1. クライアントからの接続を待つ
_listener.Start();
while (true) {
// 2. クライアントからの接続を受け入れる
var client = await _listener.AcceptTcpClientAsync();
Console.WriteLine($"Server accepted:");
var task = Task.Run(() => Receive(client));
// Taskの管理やエラー処理は省略
}
}
// 終了する
public void Close() {
_listener.Stop();
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Tcp.ConsoleApp {
public static class StreamExtensions {
// オブジェクトの読み込み
public static TObject ReadObject<TObject>(this Stream stream) {
using (var reader = new BinaryReader(stream, Encoding.UTF8, true)) {
// 長さを読み込んでから、バイト配列を読み込む
var length = reader.ReadInt32();
var bytes = reader.ReadBytes(length);
var converter = new ObjectConverter<TObject>();
return converter.FromByteArray(bytes);
}
}
// オブジェクトの書き込み
public static void WriteObject<TObject>(this Stream stream, TObject obj) {
using (var writer = new BinaryWriter(stream, Encoding.UTF8, true)) {
var converter = new ObjectConverter<TObject>();
var bytes = converter.ToByteArray(obj);
// 長さを書き込んでからバイト配列を書き込む
writer.Write(bytes.Length);
writer.Write(bytes);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment