미니 프로젝트로 네트워크와 관련된 공부를 하고 있습니다.
네트워크 공부는 처음이기에 이와 관련된 내용을 정리해 보고자 합니다✍️.
TCP Server
* 특정 IP 주소와 포트에서 클라이언트의 연결 요청을 기다리고, 연결이 수립되면 데이터를 주고받는 역할
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace TcpServerTest1
{
internal class Program
{
static void Main(string[] args)
{
NetworkStream stream = null;
TcpListener listener = null;
Socket clientSocket = null;
StreamReader reader = null;
StreamWriter writer = null;
try
{
// 1. 서버 IP 주소 설정
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
// 2. TCPListener 생성 및 시작
// TCPListener: 클라이언트의 연결을 수신
listener = new TcpListener(ipAddress, 5001);
listener.Start(); // 서버가 클라이언트의 연결 요청을 수신하도록 시작
// 3. 클라이언트의 연결 대기
// 클라이언트의 연결 요청을 받아들여 Socket 객체를 반환
// 클라이언트가 연결을 시도할 때까지 블로킹(즉, 이 코드에서 멈추고 기다림)
clientSocket = listener.AcceptSocket();
// 4. 클라이언트와의 데이터 통신을 위한 스트림 설정
// clientSocket과 연결된 NetworkStream 객체를 생성 -> 이를 통해 클라이언트와 데이터를 주고받을 수 있음
// Encoding: 데이터를 바이트로 변환하거나 바이트를 텍스트로 변환할 때 사용
stream = new NetworkStream(clientSocket);
Encoding encoding = Encoding.GetEncoding("utf-8");
// 5. 데이터 읽기와 쓰기를 위한 스트림 리더와 라이터 생성
reader = new StreamReader(stream, encoding);
writer = new StreamWriter(stream, encoding) { AutoFlush = true };
// 6. 데이터 수신 및 송신 루프
while (true)
{
// 클라이언트로부터 데이터 읽기
string str = reader.ReadLine();
Console.WriteLine(str);
// 클라이언트에게 데이터 응답
writer.WriteLine(str);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
clientSocket.Close();
}
}
}
}
TCP Client
using System.Net.Sockets;
using System.Text;
namespace TcpClientTest1
{
internal class Program
{
static void Main(string[] args)
{
TcpClient client = null;
try
{
// 1. TCPClient 생성
client = new TcpClient();
// 2. 서버에 연결 시도
// 서버가 해당 포트에서 연결 요청을 듣고 있어야 함
client.Connect("localhost", 5001);
// 3. 네트워크 스트림 획득
// 네트워크 상에서 데이터를 스트림 방식으로 송수신하는 기능을 제공
// 스트림: 데이터를 바이트 단위로 처리하며, 데이터를 차례로 읽거나 쓸 수 있는 방식
// 예를 들어, 서버가 클라이언트에 메시지를 보낼 때 NetworkStream을 통해 데이터를 읽을 수 있음
// 클라이언트는 NetworkStream을 통해 서버로 메시지를 전송할 수 있음
NetworkStream stream = client.GetStream();
// 4. 인코딩 설정
Encoding encoding = Encoding.GetEncoding("utf-8");
// 5. 스트림 리더와 라이터 생성
// StreamWriter: 데이터를 서버로 보내는 데 사용, AutoFlush: 매번 쓰기 작업 후에 버퍼를 자동으로 비움
// StreamReader: 서버로부터 데이터를 읽는 데 사용
StreamWriter writer = new StreamWriter(stream) { AutoFlush=true };
StreamReader reader = new StreamReader(stream, encoding);
// 6. 사용자 입력 받기
string dataToSend = Console.ReadLine();
while (true)
{
// 7. 서버에 데이터 전송
writer.WriteLine(dataToSend);
// 8. 종료 조건 체크
if (dataToSend.IndexOf("<EOF>") > -1)
{
break;
}
// 9. 서버로부터 데이터 읽기 및 출력
Console.WriteLine(reader.ReadLine());
// 10. 다음 데이터 입력
dataToSend = Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
throw;
}
finally
{
client.Close();
}
}
}
}
TCP Server + Multi Threading
* 기존 TCP Server에서는 Server : Client가 1:1 구조를 이루면서, 클라이언트가 2이상일 때 처리하지 못함
* 여러 클라이언트가 동시에 서버에 연결하고 통신할 수 있도록 멀티스레딩을 활용
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace TcpServerTest1
{
// 개별 클라이언트와의 통신을 처리
// 각 클라이언트는 별도의 ClientHandler 인스턴스를 가지며, 이 인스턴스는 별도의 스레드에서 실행
internal class ClientHandler
{
// 클라이언트와의 연결을 나타내는 소켓
Socket socket = null;
// 클라이언트와의 데이터 통신을 위한 스트림
NetworkStream stream = null;
// 클라이언트로부터 데이터를 읽는 리더
StreamReader reader = null;
// 클라이언트에게 데이터를 쓰는 라이터
StreamWriter writer = null;
public ClientHandler(Socket socket)
{
this.socket = socket;
}
// 클라이언트와 데이터를 주고받는 메서드
// 무한 루프를 사용하여 지속적으로 클라이언트로부터 데이터를 읽고 응답
public void Chat()
{
stream = new NetworkStream(socket);
Encoding encoding = Encoding.GetEncoding("utf-8");
reader = new StreamReader(stream, encoding);
writer = new StreamWriter(stream, encoding) { AutoFlush = true };
while (true)
{
// 클라이언트로부터 데이터 읽기
string str = reader.ReadLine();
Console.WriteLine(str);
// 클라이언트에게 데이터 응답
writer.WriteLine(str);
}
}
}
class Program
{
static void Main(string[] args)
{
TcpListener listener = null;
Socket clientSocket = null;
try
{
// 1. 서버 IP 주소 설정
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
// 2. TCPListener 생성 및 시작
// TCPListener: 클라이언트의 연결을 수신
listener = new TcpListener(ipAddress, 5001);
listener.Start(); // 서버가 클라이언트의 연결 요청을 수신하도록 시작
while (true)
{
// 프로그램이 클라이언트의 연결 요청을 받을 때마다, 새로운 Thread를 생성하고 그 스레드가 클라이언트와의 통신을 처리
// 3. 클라이언트의 연결 대기 및 수락
clientSocket = listener.AcceptSocket();
// 4. 클라이언트 처리기 생성 및 스레드 시작
ClientHandler handler = new ClientHandler(clientSocket);
Thread t = new Thread(new ThreadStart(handler.Chat)); // 새 스레드를 시작하여 클라이언트와의 통신을 독립적으로 처리
t.Start();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
clientSocket.Close();
}
}
}
}
📚 참고 자료