TCP/IP 소켓 프로그래밍 with C#
기본 용어 해석
디렉터리 서비스(directory service)는 클라이언트가 디렉터리 구조를 통해 서버가 제공하는 서비스와 서버 내부의 장소들을 열람하는 서비스.
중요 클래스
IPAddress
// IP네트워크가 갖는 하나의 인터페이스에 대한 주소를 반환
public IPAddress(long address);
public static short HostToNetworkOrder?(short);
public static int HostToNetworkOrder?(int);
public static long HostToNetworkOrder?(long);
public static short NetworkToHostOrder?(short);
public static int NetworkToHostOrder?(int);
public static long NetworkToHostOrder?(long);
public static IPAddress Parse(string address);
// 도트 표기로 된 스트링 형태의 IP주소를 IPAddress인스턴스로 변환
public static readonly IPAddress Any; // 0.0.0.0
public static readonly IPAddress Broadcast; // 255.255.255.255
public static readonly IPAddress Loopback; // 127.0.0.1
IPHostEntry?
// Dns 클래스의 GetHostByName?(), GetHostByAddress?(), GetHostEntry?()로 반환되는 컨테이너 클래스
public string HostName? { get; set; }
public string[] Aliases { get; set; }
public IPAddress[] AddressList? { get; set; }
DNS
// DNS로 부터 호스트명 또는 IP주소와 관련된 정보를 수집할 수 있는 여러 정적 메소드
public static IPHostEntry? GetHostByAddress?(IPAddress address);
public static IPHostEntry? GetHostByAddress?(string address);
public static IPHostEntry? GetHostByName?(string hostname);
public static string GetHostName?();
public static IPHostEntry? GetHostEntry?(IPAddress address);
public static IPHostEntry? GetHostEntry?(string hostname);
TcpClient?
// 소켓 하위클래스, TCP연결을 통해 다른 호스트와 접속하고, 데이터를 전송하고 수신하는데 사용되는 여러 메소드를 제공
public TcpClient?();
public TcpClient?(IPEndPoint? localEP);
public TcpClient?(string hostname, int port);
public void Close();
public void Connect(IPEndPoint? endpoint);
public void Connect(IPAddress address, int port);
public void Connect(string hostname, int port);
public NetworkStream? GetStream?();
protected Socket Client { get; set; }
public 속성으로 설정 가능한 소켓 옵션
LingerState? // 소켓의 지연시간 설정
NoDelay? // 전송이나 수신 버퍼가 꽉 차지 않을 경우 지연을 방지하는 변수값을 설정
ReceiveBufferSize? // 수신 버퍼의 크기 설정
ReceiveTimeout? // 읽기 작업시 데이터를 수신하기까지 기다리는 시간 설정
SendBufferSize? // 전송 버퍼의 크기 설정
SendTimeout? // 쓰기 작업 시작시 데이터를 송신완료까지 기다리는 시간 설정
EndPoint?
// IPEndPoint?의 추상 클래스
IPEndPoint?
// IP주소와 포트번호 형태로 TCP/IP 엔드 포인트를 나타낸다.
public IPEndPoint?(long address, int port);
public IPEndPoint?(IPAddress address, int port);
public IPAddress Address { get; set; }
public int Port { get; set; }
TcpListener?
// TCP네트워크 클라이언트로부터 들어오는 연결 요청을 대기한다.
public TcpListener?(IPEndPoint? localEP);
public TCpListener?(IPAddress address, int port);
public Socket AcceptSocket?();
public TcpClient? AccepTcpClient?();
public bool Pending(); // 수락 가능한 연결 요청 있을 경우 true
public void Start();
public void Stop();
public EndPoint? LocalEndpoint? { get; }
protected Socket Server { get; }
NetworkStream?
// Stream 클래스의 하위 클래스
public virtual void Close();
public abstract int Read(byte[] buffer, int offset, int length);
public abstract void Write(byte[] buffer, int offset, int length);
public virtual bool DataAvailable? { get; } // 스트림으로부터 읽어들일 데이터가 존재하는 경우 true
UdpClient?
public UdpClient?();
public UdpClient?(int port);
public UdpClient?(IPEndPoint? localEP);
public UdpClient?(string hostname, int port);
public void Close();
public void Connect(IPEndPoint? endpoint);
public void Connect(IPAddress addr, int port);
public void Connect(string hostname, int port);
public byte[] Receive(ref IPEndPoint? remoteEP);
public int Send(byte[] dgram, int length);
public int Send(byte[] dgram, int length, IPEndPoint? endPoint);
public int Send(byte[] dgram, int length, string honstname, int port);
protected Socket Client { get; set; }
.Net Socket
public Socket(AddressFamily?, SocketType?, ProtocolType?);
// TCP 사용시 (Address.Family.InterNetwork?, SocketType?.Stream, ProtocolType?.Tcp)
public Bind(EndPoint? localEP); // 소켓을 결합한다. IPAddress.Any(0,0,0,0)객체와 명시된 포트번호로 구성된 IPEndPoint?인스턴스를 파라미터로 받아서 로컬주소와 포트에 결합한다.
public void Close();
public Connect(EndPoint? remoteEP);
public object GetSocketOption?(SocketOptionLevel?, SocketOptionName?);
public void GetSocketOption?(SocketOptionLevel?, SocketOptionName?, byte[]);
public byte[] GetSocketOption?(SocketOptionLevel?, SocketOptionName?, int);
public void Listen(int backlog); // 연결 요청 큐의 최대 길이
public bool Poll(int microsecond, SelectMode? mode);
// 객체의 상태 확인. SelectMode?.SelectWrite? 쓰기 가능한지 확인, SelectMode?.SelectRead? 읽기 가능한지 확인, SelectMode?.SelectError? 오류 존재 여부 확인
public int Receive(byte[] buffer);
public int Receive(byte[] buffer, SocketFlags? flags);
public int Receive(byte[] buffer, int length, SocketFlags? flags);
public int Receive(byte[] buffer, int offset, int length, SocketFlags? flags);
public int ReceiveFrom?(byte[] buffer, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, SocketFlags? flags, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public int ReceiveFrom?(byte[] buffer, int offset, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public static void Select(IList readableList, IList writeableList, IList errorList, int microseconds);
public int Send(byte[] buffer);
public int Send(byte[] buffer, SocketFlags? flags);
public int Send(byte[] buffer, int length, SocketFlags? flags);
public int Send(byte[] buffer, int offset, int length, SocketFlags? flags);
public int SendTo?(byte[] buffer, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, SocketFlags? flags, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public int SendTo?(byte[] buffer, int offset, int length, SocketFlags? flags, ref EndPoint? remoteEP);
public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, byte[] optionValue);
public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, int optionValue);
public void SetSocketOption?(SocketOptionLevel? optionLevel, SocketOptionName? optionName, object optionValue);
/*
SocketOptionLeve? 열거체
옵션이 적용될 계층을 정한다.
IP
Socket
Tcp
Udp
SocketOptionName?
http://dotgnu.org/pnetlib-doc/System/Net/Sockets/SocketOptionName.html
SocketFlags?
DontRoute? 라우팅 테이블을 사용하지 않고 전송한다.
MaxIOVectorLength? 데이터를 전송하고 수신하는데 사용할 WSABUF 구조의 개수에 대한 표준수치를 제공한다.
None
OutOfBand? 대역외(out-of-band) 데이터를 처리한다.
Partial 메시지의 일부를 전송하고 수신할 수 있다.
Peek 현재 수신중인 메시지를 확인한다.
*/
public void Shutedown(SocketShutdown? how);
// 데이터를 전송하고 수신하는 기능을 중단시킨다. SocketShutdown?.Send, SocketShutdown?.Receive, SocketShutdown?.Both
public bool Connected { get; } // 가장 최근 입출력 작업후 객체가 원격 리소스에 연결되어 있는지 나타낸다.
public EndPoint? LocalEndPoint? { get; } // 로컬 엔드 포인트를 가져온다.
public EndPoint? RemoveEndPoint? { get; } // 원격 엔드 포인트를 가져온다.
SocketException?
public override int ErrorCode? { get; } // WinSock? 2.0 에러코드와 일치한다.
public virtual string Message { get; } // 사용자가 읽을 수 있는 오류 메시지
.NET 입출력 클래스
BufferedStream? // 입출력 최적화를 위한 버퍼링을 시행
BinaryReader?/BinaryWriter? // 기본 데이터 타입의 읽기/쓰기를 처리
MemoryStream? // 메모리를 백킹 스토어(backing store)로 하는 스트림을 생성하고, 임시 버퍼와 파일 대용으로 사용할 수 있다.
Stream // 모든 스트림에 대한 기본 추상 클래스
StreamReader?/StreamWriter? // 특정 인코딩을 캐릭터 입출력을 스트림에 대해서 처리
StringReader?/StringWriter? // 특정 인코딩을 캐릭터 입출력을 스트림에 대해서 처리
TextReader?/TextWriter? // 캐릭터 입출력에 대한 기본 추상 클래스로서 StreamReader?/StreamWriter?클래스와 StringReader?/StringWriter?클래스들의 부모클래스이다.
넌 블로킹 입출력
(1) 입출력 상태를 사전에 점검
TcpClient?클래스로부터 데이터를 읽어들이는 과정에서 NetStream?의 DataAvailable?속성이 true이면 읽을 데이터가 존재하는 것
TcpListener? 클래스로부터 Pending()메쏘드 사용하여 AcceptTcpClient?()메소드 또는 AcceptSocket?() 메소드를 호출하기 전에 연결 요청이 있는지를 점검 있으면 true.
Socket 클래스의 int타입의 Available속성을 통해 읽어들일 데이터의 존재 여부 확인. 네트워크로 부터 수신 했지만, 아직 읽어들이지 않은 데이터량을 바이트 단위의 정수로 기억. 즉 Available 속성이 0보다 클 경우 읽기 동작은 블록을 걸지 않는다.
(2) 블로킹 타임아웃 콜
Socket 클래스의 Poll()메소드. 메소드 인자의 시간 만큼 블록하여 요청을 대기한다.
Socket 클래스의 SetSocketOption?()메소드로 타임아웃(timeout)을 설정한다. TcpClient도? ReceiveTimeout? 속성을 통해 타임아웃을 설정할 수 있다.
(3) 넌 블로킹 소켓
Socket 클래스의 Blocking 속성을 false로 변경한다.
멀티 플렉싱
스레드
비동기 입출력
NetworkStream? 클래스의 비동기 형태 Write, Read
public override IAsynResult? BeginRead?(byte[] buffer, int offset, int size, AsyncCallback? callback, object state);
public override IAsynResult? BeginWrite?(byte[] buffer, int offset, int size, AsyncCallback? callback, object state);
public override int EndRead?(IAsyncResult? asyncResult);
public override void EndWrite?(IAsyncResult? asyncResult);
AsyncCallback? ac = new ASyncCallback?(myMethodToCall?);
...
public static void myMethodToCall?(IAsyncResult? result);
{
// 반드시 Begin과 반대되는 형으로 End해주어야 한다. 예) EndRead?(result); // 인자로 넘어온 result가 들어가야한다. 반환값은 동기함수인 Read()메소드에서 반환하는 값과 같다.
...
}
이런 콜백외의 비동기 콜작업은 효용이 없다.
다중 수신자
브로드 캐스트
사용법은 유니캐스트와 같으며 목적지 주소를 브로드캐스트 주소로 변경하면 된다.
멀티 캐스트
SocketOptionName?.MuilticastTimeToLive?옵션에서 TTL을 설정하며
SocketOptionName?.AddMembership?옵션과 SocketOptionName?.DropMembership?옵션과
UdpClient?의 JoinMulticastGroup?()메소드나 DropMulticastGroup?()메소드로 그룹을 가입하거나 탈퇴하여 멀티캐스트 메시지 수신이 가능하다.
그리고 SocketOptionName?.ReuseAddress?을 적용해야 호스트로부터 동시에 두 개이상의 멀티캐스트 메시지를 수신할수 있다.
연결 종료
버퍼 교착상태
TCP 소켓 생존 주기
TCP 연결 종료
SocketOptionName?.Linger 은 Close()메소드가 종료 핸드쉐이크를 완료되기를 대기하면 반환하지 않는 시간을 제어한다. 타임아웃을 초단위로 설정한다.
TCP연결을 종료하는데 있어 시간 대기 상태(Time-Wait State)가 있다.
소켓 종료후 네트워크상 똑같은 소켓으로 다시 생성이 되어 전송을 하여 이전 소켓으로 전송된 데이터가 네트워크상 딜레이 있을 경우 어느 소켓에서 발생된 데이터인지 오인이 생길수 있다.
이 대기 시간(quiet time)은 구현에 따라 다르게 설정되는데 적게는 30초에서 4분까지가 된다.(패킷이 네트워크상 존재할 수 있는 최대 시간의 두배)
윈도우는 4분이 기본값이다.
문제는 같은 포트로 소켓을 생성할 수 없다는 점이다. ErrorCode? 값이 10048에 해당하는 Exception을 발생시킨다.
디 멀티 플렉스
실무자를 위한 C# 네트워크 프로그래밍
C# 프로그램 컴파일및 실행
C# 프로그램 디버깅
컴파일시 실행파일에 속성변수를 설정함으로 CLR JIT컴파일러로 하여금 코드를 추적하게 만들고, 이로써 디버거에서 참고할 추적 정보를 기록할 프로그래머 데이터베이스(PDB : Programmer DataBase?) 파일을 생성하게된다.
여기서 설정되는 속성 변수는 JITTracking 플래그라 부른다. 이 플래그는 컴파일러로부터 생성된 네이티브 코드를 MSIL명령어로, 또 이 명령어를 결국 원래의 소스 코드로 디컴파일해야 한다는 정보를 CLR JIT컴파일러에 전달한다. 이 모든 정보는 실행 파일에 대한 PDB 파일에 저장된다.
dbgclr (GUI 디버거)
visual studio와 유사한 형식
cordbg (커맨드라인 디버거)
s 소스 코드 한 개 라인을 실행
si 소스 코드 한 개 라인을 실행
so 소스 코드 한 개 라인을 건너뜀
ss 다음 네이티브 또는 IL 명령을 실행
p <arg> 변수 현재 값을 출력
pro 프로그램 실행에 대한 시스템 프로세스 정보를 출력
reg 현재 쓰레드에 대한 CPU 레지스터 정보 출력
run <prog> 디버거에서 prog 프로그램을 실행
break 소스에 브레이크지점 설정 또는 출력
sh 현재 실행중인 코드 라인과, 전후 5개 라인을 출력
IL DASM(Microsoft Intermediate Language Disassembler)
네트워크 트래픽 관리
WinPcap? 드라이버
Politecnico di Torino 의 NetGroup?팀이 개발한 모든 윈도우즈 시스템에서 네트워크 패킷을 수집할 수 있도록 설계.
WinDump?
커맨드 라인 옵션
-a 네트워크와 브로드캐스트 주소를 네임으로 변경
-B size 수신 버퍼 크기를 size로 수정
-c count 패킷을 count만큼만 수집
-D 시스템에서 사용가능한 모든 네트워크 인터페이스를 표시
-e 각 출력줄에 링크 레벨의 정보를 출력
-F file File 파일에 정의된 필터를 적용
-i interface interface네트워크를 모니터링 하는데, interface는 인터페이스 이름 혹은 ?D 명령어로 지정된 인터페이스 번호가 될 수 있다.
-n 주소를 이름으로 변화하지 않도록 설정
-N FQDN(Fully Qualified Domain Name)을 출력하지 않도록 설정
-q 간단한 형태로 패킷 정보를 출력
-r file 덤프 파일 file로부터 패킷을 읽어들임
-S 절대 TCP일련 번호를 출력
-s snaplen 패킷으로 부터 snaplen 만큼의 바이트를 수집. 기본 수치는 68이다.
-t 각 라인에 시간을 출력하지 않음
-w file 결과를 file에 출력
-X 각 패킷을 hex형태와 ASCII형태로 출력
-x 각 패킷을 hex형태로 출력
필터 조건문 적용
windump ip // 네트워크에 흐르는 ip패킷만을 수집
windump ip host 203.241.228.251 // 특정 ip주소로부터 오가는 네트워크 패킷 수집
windump ip src 203.241.228.251 // 특정 ip주소로부터 오는 네트워크 패킷 수집
windump ip host 203.241.228 // 특정 서브넷으로부터 오가는 패킷 수집
Analyzer
네트워크 패킷 분석
이더넷 계층
IP 계층
DDN(Dotted Decimal Notaion)으로 표현하면
TCP 계층
- 3 방향 핸드 쉐이크 (three-way handshake)
- 소스 호스트는 세션의 시작을 알리기 위해 Syn 플래그를 전송한다.
- 목적지의 호스트는 SYN플래그와 ACK플래그를 같은 플래그에 담아 세션의 시작에 동의 하겠다는 응답을 전송한다.
- 소스 호스트는 다시 ACK플래그를 전송하여 세션을 확립하고 패킷을 대기한다.
- 3 방향 핸드 쉐이크 (three-way handshake)
- 세션을 닫고자 하는 호스트 측에서 FIN플래그를 전송한다.
- 수신측의 호스트는 ACK플래그와 FIN플래그를 같은 패킷에 담아 세션의 종료에 동의하겠다는 응답을 전송한다.
- 처음의 호스트는 다시 ACK플래그를 전송하여 정식으로 세션을 종료한다.
UDP 계층
IP 주소 정보 찾기
1. IPConfig 사용
2. 레지스트리 사용
3. WMI 사용 (Windows Management Instrumentation)
마이크로소프트가 구현한 Web-Based Enterprise Management(WBEM)의 한종류 이다. WBEM은 Distributed Management Task Force. Inc(DMTF) 에서 개발한 것으로 네트워크 환경에서 시스템 정보를 접근하는 표준
윈도우 2000이후, 지원하고 이전 윈도우즈는 WMI Software Developers Kit 를 통해 WMI 시스템을 별도로 설치 할 수 있다. (http://www.microsoft.com/downloads/search.asp?로 부터 다운)
WMI 내에 있는 WMI Win32_NetworkAdapterConfiguration? 표는 시스템에서 설치된 네트워크 장치와 관련된 정보를 기록하고 있다.
using System.Management;
...
ManagementObjectSearcher query = new ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = 'TRUE'");
ManagementObjectCollection queryCollection = query.Get();
foreach(ManagetmentObject mo in queryCollection)
{
...
Console.WriteLine( (string[])mo["IPAddress"] );
}
네트워크 상에서의 데이터 이동
BitConverter? Class
public static byte[] GetBytes([TYPE] value); // 특정 타입을 바이트 배열로 반환
public static [TYPE] To[TYPE](byte[] value, int startIndex); // 바이트를 특정 타입으로 변환
// string형은 Encoding 클래스의 Encoding.ASCII.GetString()함수 사용
Buffer Class // 기본 형식 배열을 조작
public static void SetByte(Array array, int index, byte value); // 배열 특정 위치의 값을 변경
public static void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count); // 배열의 블록 단위 복수
프로세스
public static Process GetCurrentProcess();
public static Process[] GetProcesses(); // 실행중인 프로세스 전부를 찾는다.
public static Process GetProcessById(int); // 실행중인 프로세스 찾기
public static Process[] GetProcessesByName(string); // 실행중인 프로세스 찾기
public static Process Start(string); // 오버로드 되어있음. 프로그램 이름으로 실행하는 방법외에 인자 넣을수도 있고 ProcessStartInfo 클래스에 정보 채워서 실행 가능.
public void Kill(); // 프로세스 강제 종료
public bool CloseMainWindow(); // 메인 윈도우에 닫기 메시지를 전송하여 종료를 권유. 메인 윈도우가 없거나 모달 대화상자일경우 실패하기도 한다.
public void Close(); // 프로세스 종료. 강제 종료가 아니라 연결된 리소스 전부를 해제후 종료.
스레드
Process 클래스의 속성을 통해 Collection을 얻어온다.
public ProcessThreadCollection Threads {get;}
public Thread(ThreadStart start); //스레드 생성자 델리게이트형 ThreadStart를 요구한다.
public delegate void ThreadStart(); // 실행할 스레드 메소드를 지정한다.
public void Start();
public Thread(ParameterizedThreadStart start); // ParameterizedThreadStart로 등록하면
[ComVisibleAttribute(false)]
public delegate void ParameterizedThreadStart(Object obj);
public void Start(Object state); // 스레드에 Object형 매개변수를 전달할 수 있다.
스레드 풀
ThreadPool? class
작업 항목 게시, 비동기 I/O 처리, 다른 스레드 대신 기다리기 및 타이머 처리에 사용할 수 있는 스레드 풀을 제공합니다.
생성자가 없고 모든 메소드는 정적이다.
메소드가 호출되면 운영체제는 자동으로 스레드 풀을 생성하고 25개의 스레드가 등록 가능한 상태가 된다.
타이머 큐 타이머와 등록된 대기 작업도 스레드 풀을 사용된다.
타이머 큐 타이머와 등록된 대기 작업의 콜백 함수는 스레드 풀에 대기하게 된다.
스레드 풀에 있는 스레드에서 작업 항목을 처리하도록 요청하려면
public static bool QueueUserWorkItem(WaitCallback callBack);
public static bool QueueUserWorkItem(WaitCallback callBack, Object state);
[Serializable]
public delegate void WaitCallback(object state);
사용예)
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc,state));
를 호출하여 사용하게된다. 작업항목을 큐에 대기시킨 후에는 취소할 수가 없다.
이하 시스템 프로그래밍 관련 부분...
// 제한 시간(밀리초)에 부호 있는 32비트 정수를 사용하여 WaitHandle을 기다리는 대리자를 등록합니다.
public static RegisteredWaitHandle RegisterWaitForSingleObject(WaitHandle, WaitOrTimerCallback, object, int, bool);
// WaitHandle 클래스
공유 리소스에 대한 단독 액세스를 기다리는 운영 체제 관련 개체를 캡슐화합니다
EditText of this page (last modified 2008-11-05 11:01:00)
FindPage by browsing, searching, or an index
Or try one of these actions: DeletePage, DeleteUploadedFile, LikePages, SpellCheck, UploadFile
'ByteCode > C#' 카테고리의 다른 글
IP 주소 관련 클래스 (0) | 2010.03.18 |
---|---|
TCP Socket (0) | 2010.03.18 |
Stream, Dgram 설명 (0) | 2010.03.18 |
데이터 전송 (0) | 2010.03.18 |
파일 송수신 (0) | 2010.03.18 |