- 서버측에서의 Binding까지는 UDP 서버 소켓 사용하는 방법과 동일함.
- TCP/IP 통신을 할 때 Socket의 생성자는 아래와 같이 지정한다.
Socket socket =
new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- TCP/IP 서버는 아래와 같이 동작함.
Binding -> Listen(연결 받을 수 있는 상태) -> Accept(클라이언트 접속 큐에서 하나 꺼내옴)
- TCP/IP에서는 Send / Receive를, UDP에서는 SendTo / ReceiveFrom을 사용하여야 한다. 이미 Accept로 클라이언트 소켓 정보를 알고있으므로, Send / Receive에는 접점 정보를 알아내기 위한 IPEndPoint 인자가 없다.
[ TCP 서버측 소켓 ]
private static void serverFunc(object obj)
{
using (Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
IPEndPoint clientEP = new IPEndPoint(IPAddress.Any, 10200);
serverSocket.Bind(clientEP);
serverSocket.Listen(10); // 10 : 클라이언트 연결 큐 크기
while(true)
{
Socket clientSocket = serverSocket.Accept();
byte[] receiveByte = new byte[1024];
int nRecv = clientSocket.Receive(receiveByte);
string txt = Encoding.UTF8.GetString(receiveByte, 0, nRecv);
byte[] sendByte = Encoding.UTF8.GetBytes("Hello:" + txt);
clientSocket.Send(sendByte);
clientSocket.Close(); // Close 꼭 해줄 것.
}
}
}[ TCP 클라이언트측 소켓 ]
- UDP와의 차이점은, Connect 함수가 추가된다는 것.
private static void clientFunc(object obj)
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
EndPoint serverEP = new IPEndPoint(IPAddress.Loopback, 10200);
socket.Connect(serverEP); // TCP/IP에서, Client가 Server 연결시 필요
byte[] buf = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
socket.Send(buf);
byte[] receiveBytes = new byte[1024];
int nRecv = socket.Receive(receiveBytes);
string txt = Encoding.UTF8.GetString(receiveBytes, 0, nRecv);
Console.WriteLine(txt);
}
Console.WriteLine("TCP/IP Client Closed.");
}[ TCP 서버 개선 - 다중 스레드와 비동기 통신 ]
- 그러나 이러한 방식은 Send/Receive에서 blocking되므로 빠르게 Accept 할 수 없다는 단점이 있다. 개선을 위해, Accept로 받은 클라이언트 처리를 별도의 스레드에 위임할 수 있다. 클라이언트 하나 당 하나의 스레드가 필요하므로 부하가 가중될 수도 있다.
private static void serverFunc(object obj)
{
using (Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 10200);
serverSocket.Bind(clientEP);
serverSocket.Listen(10);
while (true)
{
Socket clientSocket = serverSocket.Accept();
ThreadPool.QueueUserWorkItem(clientSocketProcess, clientSocket);
}
}
}
private static void clientSocketProcess(object state)
{
Socket clientSocket = state as Socket;
byte[] receiveBytes = new byte[1024];
int nRecv = clientSocket.Receive(receiveBytes);
string txt = Encoding.UTF8.GetString(receiveBytes, 0, nRecv);
clientSocket.Send(Encoding.UTF8.GetBytes(txt));
clientSocket.Close();
}
- 위 단점은 비동기 통신으로 해결할 수 있다.
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
class Program
{
static void Main(string[] args)
{
Thread serverThread = new Thread(serverFunc);
serverThread.IsBackground = true;
serverThread.Start();
Thread.Sleep(500); // 소켓 서버용 스레드가 실행될 시간을 주기 위해
Thread clientThread = new Thread(clientFunc);
clientThread.IsBackground = true;
clientThread.Start();
Console.WriteLine("종료하려면 아무키나 누르세요.");
Console.ReadLine();
}
public class AsyncStateData
{
public byte[] buffer;
public Socket socket;
}
private static void serverFunc(object obj)
{
using (Socket srvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 10200);
srvSocket.Bind(endPoint);
srvSocket.Listen(10);
while(true)
{
Socket clientSocket = srvSocket.Accept();
AsyncStateData data = new AsyncStateData();
data.buffer = new byte[1024];
data.socket = clientSocket;
// public IAsyncResult BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state)
clientSocket.BeginReceive(data.buffer, 0, data.buffer.Length, SocketFlags.None, asyncReceiveCallback, data);
}
}
}
public static void asyncReceiveCallback(IAsyncResult asyncResult)
{
AsyncStateData receiveData = asyncResult.AsyncState as AsyncStateData;
int nRecv = receiveData.socket.EndReceive(asyncResult);
string txt = Encoding.UTF8.GetString(receiveData.buffer, 0, nRecv);
byte[] sendBytes = Encoding.UTF8.GetBytes("Hello:" + txt);
receiveData.socket.BeginSend(sendBytes, 0, sendBytes.Length, SocketFlags.None, asyncSendCallback, receiveData.socket);
}
public static void asyncSendCallback(IAsyncResult asyncResult)
{
Socket socket = asyncResult.AsyncState as Socket;
socket.EndSend(asyncResult);
socket.Close();
}
private static void clientFunc(object obj)
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
EndPoint serverEP = new IPEndPoint(IPAddress.Loopback, 10200);
socket.Connect(serverEP); // TCP/IP에서, Client가 Server 연결시 필요
byte[] buf = Encoding.UTF8.GetBytes(DateTime.Now.ToString());
socket.Send(buf);
byte[] receiveBytes = new byte[1024];
int nRecv = socket.Receive(receiveBytes);
string txt = Encoding.UTF8.GetString(receiveBytes, 0, nRecv);
Console.WriteLine(txt);
}
Console.WriteLine("TCP/IP Client Closed.");
}
}
- 인터넷이 갑자기 끊기거나 등의 상황이 있을 수 있다. Receive/Send를 호출할 때는 try/catch로 SocketException을 처리할 수 있도록 구현해야 한다.
출처 : 시작하세요! C# 7.1 프로그래밍(위키북스, 정성태님 저)
'Programming > C#' 카테고리의 다른 글
| MSSQL Database 연동(2) (0) | 2019.03.11 |
|---|---|
| MSSQL Database 연동(1) (0) | 2019.03.05 |
| app.config (0) | 2019.03.05 |
| [네트워크 프로그래밍] Http 통신 (0) | 2019.03.02 |
| [네트워크 프로그래밍] UDP 예제 (0) | 2019.02.27 |