명세서를 받고 이틀이 지났습니다. 그동안 나초보씨는 무엇을 했을까요? 짜짠~ 간단한 서버, 클라이언트 어플리케이션을 작성했습니다. MSDN이 제공하는 예제를 조금 고친 것에 불과하지만, 처음 C#을 다뤄보는 나초보씨의 입장에서는 대단한 성과가 아닐 수 없습니다. 나초보씨 스스로도 자신이 만들어낸 작품에 만족하는 듯 합니다. 그럼 나초보씨의 첫번째 C# 코드를 다 같이 감상해 보겠습니다.
‘이보다 간단할 순 없다’ 서버 만들어보기
public class EntryPoint
{
[STAThread]
static void Main(string[] args)
{
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
IPAddress localAddr = IPAddress.Any;
// TcpListener server = new TcpListener(port);
TcpListener server = new TcpListener(localAddr, port);
// Start listening for client requests.
server.Start();
// Enter the listening loop.
while(true)
{
Console.WriteLine("(1) 서버가 클라이언트가 접속하기를 기다린다.......");
// Perform a blocking call to accept requests.
// You could also user server.AcceptSocket() here.
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("(2) 클라이언트가 접속했다!");
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
BufferedStream bufferedStream = new BufferedStream(stream);
Console.WriteLine("(3) 서버가 클라이언트에게 32비트 정수의 가장 큰 값을 보낸다. 이때 정수값을 Network-byte order로 전송한다.");
BinaryWriter bw = new BinaryWriter(bufferedStream);
bw.Write( IPAddress.HostToNetworkOrder(int.MaxValue) );
bw.Flush();
Console.WriteLine("Send: " + int.MaxValue);
Console.WriteLine("(6) 서버는 클라이언트에게서 받은 정수값을 출력한다.");
BinaryReader br = new BinaryReader(bufferedStream);
Console.WriteLine("Recv: " + IPAddress.NetworkToHostOrder(br.ReadInt32()));
// Shutdown and end connection
Console.WriteLine("(7) 연결 종료한다.");
client.Close();
}
}
catch(SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("
Hit enter to continue...");
Console.Read();
}
}
‘이보다 간단할 순 없다’ 클라이언트 만들어보기
class Class1
{
[STAThread]
static void Main(string[] args)
{
try
{
// Set the TcpListener on port 13000.
Int32 port = 13000;
TcpClient client = new TcpClient();
client.Connect("localhost",port);
Console.WriteLine("(2) 클라이언트가 접속했다!");
NetworkStream stream = client.GetStream();
BufferedStream bufferedStream = new BufferedStream(stream);
Console.WriteLine("(4) 클라이언트는 서버에게서 받은 정수값을 출력한다.");
BinaryReader br = new BinaryReader(bufferedStream);
Console.WriteLine("Recv: " + IPAddress.NetworkToHostOrder(br.ReadInt32()));
Console.WriteLine("(5) 클라이언트는 서버에게 32비트 정수의 가장 작은 값을 보낸다. 마찬가지로 Network-byte order byte로 전송한다.");
BinaryWriter bw = new BinaryWriter(bufferedStream);
bw.Write( IPAddress.HostToNetworkOrder(int.MinValue) );
bw.Flush();
Console.WriteLine("Send: " + int.MinValue);
// Shutdown and end connection
Console.WriteLine("(7) 연결 종료한다.");
client.Close();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.Read();
}
}
}
우선 두개의 어플리케이션이 무엇을 하는지 알아보겠습니다.
-
서버가 클라이언트가 접속하기를 기다린다.
-
클라이언트가 접속한다.
-
서버가 클라이언트에게 32비트 정수의 가장 큰 값(231-1)을 보낸다. 이때 정수값을 Network-byte order로 전송한다.
-
클라이언트는 서버에게서 받은 정수값을 출력한다.
-
클라이언트는 서버에게 32비트 정수의 가장 작은 값(-231)을 보낸다. 마찬가지로 Network-byte order byte로 전송한다.
-
서버는 클라이언트에게서 받은 정수값을 출력한다.
-
연결 종료한다.
여기서 잠깐! Network-byte order가 뭐냐?
어떤 언어로든 네트워크 프로그래밍하게 되면, Network-byte order라는 것과는 안면을 트게 됩니다. 세상에는 두 종류의 CPU가 있습니다. 하나는 큰 형님(Big Endian)이고, 다른 하나는 작은 아우(Little Endian)입니다. Sun Microsystems의 Sparcs CPU는 역시 커다란 서버에 쓰이는만큼 큰 형님이시고, Intel의 x86 CPU는 조그마한 PC에 많이 쓰이는지라 작은 아우입니다. 영국 귀족은 프랑스어를 쓰고, 하층민은 영어를 쓰던 시절이 있었습니다. 마찬가지로 큰 형님과 작은 아우는 서로 다른 방식으로 데이터를 표기합니다. 이를테면 큰 형님이 1010이라고 말하는 것을 작은 아우는 0101이라고 말합니다. 그런 까닭에 서로 말이 통하지 않았습니다. 결국 큰 형님들은 말합니다.
작은 아우들아. 앞으로 큰 형님들과 이야기할 때는 큰 형님들의 말을 하도록 해라.아마도 큰 형님들은 아랫 것들의 말을 배우기 싫었나 봅니다. 이리하여 네트워크 세계에서는 Big Endian이 공통 언어로 자리잡게 되었습니다.참고 자료
헉헉. 숨이 차는군요. 페이지도 제법 길어진 것 같은데, 저 뿐만 아니라 나초보씨의 실전 C# 네크워크 프로그래밍을 읽고 계신 독자분들도 힘들 것 같습니다. 이쯤에서 일단 접고, 나중에 계속 설명하겠습니다. 다음편을 기대해주세요.