Skip to content

Instantly share code, notes, and snippets.

@jacking75
Forked from ichiroku11/Client.cs
Last active July 8, 2020 05:44
Show Gist options
  • Save jacking75/8fa083cf407a41d7d268c971bce519dc to your computer and use it in GitHub Desktop.
Save jacking75/8fa083cf407a41d7d268c971bce519dc 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 = "ABCDE" }),
new Client<Message, Message>(endpoint).Send(new Message { Id = 20, Content = "12345" }),
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>();
reProgramturn 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