2007. 11. 20. 15:26

DevX Guide to Windows Socket Programming

  WinSock, 윈도우소켓은 Unix의 소켓에 근본을 두고있다.소켓은 기종이 같거나 다른 근/원거리의
컴퓨터에서 상호통신을 위해서 고안되었다. 각각의 컴퓨터에서 연결이 필요하다면 단지 소켓으로
연결해서 사용하면 되는 간단한 구조이다.

소켓은 TCP/IP프로토콜을 근간으로 한다. TCP/IP프로토콜은 OSI7 계층중 상위계층에 속하는 레이어이다.
HTTP / FTP / TELNET 등의 프로토콜이 TCP/IP로 구성되어있다.
소켓자체는 이러한(HTTP/FTP...) 프로토콜을 전혀 이해하지 못한다. 당연히 내부에 어떠한 내용이 있는지
또한 모른다. 단지 접속한 컴퓨터에게 데이터를 보내거나 받는것에만 신경쓰는 것이다.



::: 소켓타입 (Type of Socket) :::

소켓에는 2종류가 있다. 스트림(Stream)과 데이터그램(Datagram) 소켓이다.
먼저 스트림소켓은 TCP를 사용하며, 연결지향적인 서비스를 제공한다. 대용량의 자료를 TCP를 통해서
보내게 되면 데이터는 순차적으로 보내지게 되며 안정적으로  작동하게 된다.(데이터패킷) 
이에 반해 데이터그램은 간단한 전송메커니즘이다. 비연결지향적이고 순서적으로 데이터를 보내지도
않는다. 단순히 무작위로 데이터조각을 보내버리고 확인도 하지 않는다. (UDP가 사용)
당연히 대부분은 TCP를 사용하게된다(HTTP/FTP..)
간단히, 스트림소켓은 전화통신과 같은 맥락이다. 상대방에게 전화를 걸고 상대방이 전화를 받게되면
안정적으로 통화가 가능하다(통신사의 사정은 제외하자!)
만약 통화중에 문제가 발생한다면 즉시 문제를 감지할 수 도 있을 것이다.
데이터그램(UDP)은 불특정 다수에게 보내는 스펨메일과 유사하다. 상대가 수신하는지 여부는 전혀
관여하지 않고 단지 해당주소에 스펨내용을 전송하면 끝이다. 단지 비유일뿐이다, 실제 스팸메일이
UDP를 쓴다고 주장하지는 말기 바란다.

소켓모델은 대부분 Client / Server 모델로 구현된다.


::: 전송순서 (Byte Ordering) :::

어셈블리를 접하면서 혹시 리틀엔디언과 빅엔디언에 대해서 들어본 적이 있는가?
x86머신은 리틀엔디언방식으로 작동한다. 하지만 대형 유닉스시스템은 빅엔디언 시스템이다. (모토롤라칩)
리틀엔디언방식은 메모리에 데이터를 저장할때 낮은쪽의 주소에 상위바이트를 저장하는 방식이다.
예를 들어 Var1 DW 02B34H 라는 데이터셀이 실제 메모리에 저장될때는 빅엔디언방식에서는 2B 34로
저장되지만, 리틀엔디언 방식에서는 34 2B 순으로 저장된다. 리틀엔디언방식은 프로그래머를 괴롭힌다.
왜 이렇게 했을까? 이유가 있다. 하위호환성때문이다. CPU의 레지스터는 32bit환경에서 예를 들면
EAX 레지스터는 32비트값을 저장할 수 있다. 하지만 16비트환경일때는 AX레지스터로 사용했고, 8비트환경
일때는? AH / AL로 사용했다. 가장 하단은 AL레지스터일것이다.
그래서 환경이 변화되어도 항상 AL 값이 동일 할려면 어쩔수 없이 리틀엔디언 방식으로 사용하게 된것이다.

다시 본론으로 와서, IP주소가 205.34.67.24라면 빅엔디언방식에서는 순서그대로 저장이 되어 사용되어
지지만, 리틀엔디언에서는 24.67.34.205로 저장된다.!
소켓에서는 이런 구조에 당연히 신경써줘야 정상적으로 통신이 가능할 것이다.(소켓은 빅엔디언!)



::: 블러킹 / 넌블러킹 방식 (Blocking / Non-Blocking) :::

소켓의 작동 방식에는 크게 2종류가 있다. 블럭킹/넌블러킹(Blocking/Non-Blocking) 방식이다.
오리지날 소켓인 버클리소켓은 블러킹모드로 작동된다.
단순히, 블러킹모드는 어떠한 작업이 완료되기 전까지는 제어를 반환하지 않는다. 즉, 파일을 전송하라고 
명령을 내리면 전송작업이 완료될 때 까지 아무작업도 못한다. 넌블러킹은 이와는 반대이다.
그래서 블러킹모드는 동기화처리 방식이고, 넌블러킹은 비동기처리 방식이다. 대용량자료를 전송중에
 문제가 발생하면 시스템이 정지한 상태로 변한다.(당연하다. 오지않는 사람을 기다리는것과 같다)
이러한 오리지날 블러킹모드는 윈도우 환경에서는 허용하지 않는다!  그래서, 윈도우 환경에서는
비동기모드로 작동하는 넌블러킹 모드를 제공한다.  리틀엔디언 빅엔디언과 같이, 비합리적으로 보여도
각각 장단점이 있기때문이다. 또한, 윈도우에서는 넌블러킹모드를 최적으로 사용하도록 최적화 작업을
해놓았기 때문에 넌블러킹모드로 사용하면 된다.(시스템의 문제지 프로그래머의 문제가아니다)

어셈블리의 예를 들면 윈도우메시지 큐에서 메세지를 가져오는 GetMessage방식은 메세지를 꺼내기
전까지는 제어를 반환하지 않는 블러킹방식이라 생각하면 되고, PeekMessage는 넌블러킹 방식으로 항상
대기하지 않고 메세지가 있나없나만 확인하고 곧바로 제어를 반환하는 형식과 유사하다. 어렵게 생각하지
말기 바란다.



::: 포트 (Port) :::

원격(로컬) 컴퓨터에 접속이 되고 소켓이 생성될때, 소켓은 반드시 어떤 서비스와 연결될지를 결정하는
포트를 지정해야한다. 포트는 컴퓨터에 부착된 COM1/COM2포트 와 유사하다. 윈속에서 사용하는 가상의
하드웨어 포트라고 생각하면 된다. 예를 들어, 서버소켓이 포트번호 21번에서 접속을 항상 대기하고 있다.
(FTP의 경우)  웹서버는 80번 포트에서 대기하고 있다는 식이다. 1024번 이하는 시스템에서 사용하게된다.
소켓은 바로 IP:Port로 구성되어있다. 어느 컴퓨터의 어떤 서비스에 연결할지를  정하고 연결되면 데이터를
보내거나 받는 구조이다. IP:Port를 소켓이라고 부른다. 이때 데이터를 안정적으로 보낼려면 TCP를 사용
하고, 받든지 말든지 그냥 전송만 하는 경우는 UDP를 사용한다는 것만 알면된다. 실제 내부 전송방식은
복잡하지만, 지금은 개념만 숙지해 두기 바란다.


::: 윈속 프로그래밍의 순서 (WinSock Programming Overview) :::

윈속프로그래밍의 순서는 대략적으로 다음과 같은 7가지 단계를 거치게 된다.

  1. winsock.dll 초기화
  2. Socket 생성
  3. 작동모드 선택 : 블러킹 / 넌블러킹
  4. Socket 접속
  5. 데이터 전송 / 수신 작업
  6. Socket 접속해제
  7. winsok.dll 해제

각 단계별로 하나씩 알아보도록 하자.



1. winsock.dll 초기화
winsock.dll을 초기화 하기 전에, 반드시 WSAStartup 프로시져를 선언해줘야한다.!
WSAStartup프로시져는 다음과 같은 프로토타입을 가지고 있다.

 WSAStartup PROTO wVersionRequire:DWORD, LPWSADATA:DWORD

wVersionRequired : 사용하고자하는 WinSock의 버전. 버전에는 1.1과 1.2가 존재한다.
                             버전 1.1은 윈도우95에 탑재되었었고, 1.2는 윈도우NT에 탑재되었었다.
                            1.1버전을 사용하고 싶으면, 101H로 선언하고, 200H는 1.2버전이다.
lpWSADATA         : WSADATA 구조체의 포인터로서 winsock.dll이 내용을 채워준다. 이구조체가
                            WinSock.dll의 실제 구현체이다. 일반적으로, WSADATA구조체의 주소만 지정해
                           주면 된다.

이 프로시져가 실패하게 되면 어떠한 WinSocket함수도 사용할 수 없다. 성공적으로 수행되면 winsock.dll이
WSADATA를 알맞게 채워주게 될 것이다. 어플리케이션이 1.1버전을 원한다면 WSADATA는 크게 쓸모가
없을 것이다.

코드샘플

.data
wsadata WSADATA <>
.......
.code
invoke WSAStartup, 101H, addr wsadata
.if eax != NULL
  <에러처리 루틴>
.else
  < 초기화작업 성공. 윈속함수 사용 루틴 >
.endif



2. Socket 생성
WSAStratup프로시져가 성공적으로 수행 된 후에는 소켓을 생성해야 한다.

   socket PROTO af:DWORD, type:DWORD, protocol:DWORD

af           :  주소형식을 지정. 현재 사용할 수 있는 형식은 AF_INET 뿐이다.
type        : 소켓타입을 지정한다. 상기에서 설명한 스트림과 데이터그램을 지정한다.
                SOCK_STREAM 과 SOCK_DGRAM을 지정해서 사용하게 된다.
protocol   : AF_UNSPEC이 지정되면 프로토콜을 반드시 지정해야하고, af 값이 AF_INET
                라면 "0"을 지정하면 된다.

접속해서 원활히 통신하려면 적어도 한개의 소켓은 생성 되어야 한다. 성공적으로  수행된 후 소켓을
제어하는 소켓핸들을 반환하게 되고 프로그램에서 사용하면 된다.


코드샘플

.data?
sock dd ?
.....
.code
.....
invoke socket, AF_INET, SOCK_STREAM, 0  ;스트림소켓 생성
.if eax != INVALID_SOCKET
    mov sock, eax
.else
    invoke WSAGetLastError
    ....
.endif



3. 작동모드 선택 : 블러킹 / 넌블러킹
소켓이 생성되었다면 기본적으로 블러킹 모드로 설정되어 있을 것이다. 이모드를 넌블러킹 모드로 설정하고
싶다면, setsockopt / WSAAsyncSelect를 사용해서 변경해야한다. 일반적으로, WSAAsyncSelect를
사용해서 블러킹/넌블러킹 모드를 선택하게 된다. setsockopt 프로시져가 사용법이 어려워서가 아니다.
단지 선택일 뿐이다. 이 프로시져는 소켓의 여러가지 특성을 조절할 수 있는 방법을 제공해 준다.

WSAAsyncSelect PROTO socket:DWORD, hwnd:DWORD, msg:DWORD, Event:DWORD

socket      : socket 프로시져에서 반환한 소켓 핸들을 지정한다.
hwnd        : 윈속에서 발생한 이벤트를 받게될 윈도우 핸들을 지정한다.
msg          : 사용자가 원하는 메세지를 지정할 수 있다.
Event        : 윈속이벤트이다. 다음과 같은 이벤트들이 있다.
                FD_READ     : 읽기 이벤트
                FD_WRITE    : 쓰기 이벤트
                FD_OOB      : 범람데이터 이벤트 (긴급메세지)
                FD_ACCEPT   : 접속 허용 이벤트
                FD_CONNECT  : 접속완료 이벤트
                FD_CLOSE    : 소켓 종료 이벤트

성공적으로 수행되면 NULL을 반환한다. 실패하면 SOCKET_ERROR를 반환하며, WSAGetLastError
프로시져로 실제 에러코드를 찾아보면 된다.

winsock.dll은 실제 상기와 같은 이벤트를 프로그램에게 보내게 된다. 만약 이러한 이벤트를 프로그램에
보내고 싶지 않다면, WSAAsycSelect의 iEvent인자에 "0"을 설정 하면 된다. 이러한 메세지는
PeekMessage함수를 PM_REMOVE 플래그를 설정해서 읽으면 된다.  윈속 이벤트가 발생하면 hwnd로
지정된 윈도우 프로시져에 msg를 보내주게 되는 형식이다. msg의 wParam은 소켓설명자가 설정되고,
lParam의 상위워드에는 에러가 발생했을 경우 에러코드가 설정되며, lParam의 하위워드에는 이벤트가
설정된다.

코드샘플

.data?
hwnd dd ?          ; 윈속메세지를 받을 윈도우 핸들
socket dd ?        ; 소켓설명자
....
.const
WM_SOCKET equ WM_USER+100      ; WM_SOCKET라는 사용자 정의 메세지 선언
....
.code
....
invoke WSAAsycSelect, socket, hwnd, WM_SOCKET, FD_CONNECT+FD_READ+FD_CLOSE
.if eax == SOCKET_ERROR
  < 에러처리 루틴 >
.else
 .....
.endif

WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
....
.if uMsg == WM_SOCKET          ; WSASycSelect에서 발생한 메세지
    mov eax, lParam
    .if ax == FD_CONNECT          ; 이벤트코드 lParam의 하위워드
        shr eax, 16                      ; lParam의 상위워드의 에러코드 검출
        .if ax == NULL
            < 해당 처리 루틴 : 에러없음 >
        .else
            < 에러 처리 루틴 >
        .endif
    .elseif ax == FD_CLOSE
         shr eax, 16
         .if ax == NULL
            < 해당 처리 루틴 : 에러없음 >
         .else
            < 에러 처리 루틴 >
         .endif
     .endif
.else
....
.endif
WndProc ENDP



4. Socket 접속
소켓이 생성되면 두가지 선택을 할 수 가 있다. 접속을 대기 하거나 원격소켓에 접속하는 것이다.
만약 접속을 대기하려면, listen 프로시져를 호출해서 접속을 대기 해야하며 원격 호스트에서 접속요청이
오면 accept 프로시져를 호출해서 접속을 맺어야 한다. 만약 원격호스트에 접속하려면, connect
프로시져를 호출해서 접속하면 된다.

connect PROTO socket:DWORD, lpSockAddr_in:DWORD, namelen:DWORD

socket            : 소켓설명자로서 로컬소켓을 의미한다. socket 프로시져에서 반환된 값이다
lpSockAddr_in : SOCKADDR_IN 구조체의 포인터로서 다음과 같이 선언되어있다.
                Sockaddr_in STRUCT
                  sin_family WORD ?
                  sin_port   WORD ?
                  sin_addr   DWORD ?
                  sin_zero   BYTE 8 dup(?)
                Sockaddr_in ENDS
                connect 프로시져 호출전에 반드시 이 구조체에 값을 설정해야한다.
sin_family       : socket함수와 같다. 항상 AF_INET 이다.
sin_port         : 원격호스트에서 사용할 포트번호 이다. 이것은 상위 프로토콜에 의존적이다.
                     HTTP에 접속할려면 80이고, FTP에 접속할려면 21등이다.
                     반드시 알아야할 것이 있다. 바이트순서가 반드시 빅엔디언이여야 한다.
                    그래서, htons 프로시져를 사용해서 빅엔디언으로 변환해야한다.
                    윈속프로그래밍에서 가장 빈번하게 발생하는 에러중 하나 이므로 명심하기 바란다.
sin_addr        : 원격호스트의 IP주소이다. 다시한번 상기하기 바란다. 빅엔디언으로 변환!!
sin_zero        : 예약영역이고 손댈것 없다!
namelen        : SOCKADDR_IN 구조체의 크기

소켓의 모드(블러킹/넌블러킹)에 따라서 반환값이 다르다.
* 넌블러킹모드 : SOCKET_ERROR를 반환하면 WSAGetLastError 프로시져로 에러코드를 검사 하면된다.
                       만약 에러코드가 WSAEWOULDBLOCK 라면, 즉시 제어를 반환하지 않음을  의미한다. 
                      이것은 에러코드가 아님을 유의하기 바란다.
* 블러킹 모드 : 성공 / 실패에 대한 응답을 한다


코드샘플

.data
sin SOCKADDR_IN <>
IPAddress db "206.24.234.23", 0
Port      dd  80                             ; HTTP예제

.data?
socket dd ?

.code
....
mov sin.sin_family, AF_INET
invoke htons, Port                           ; 빅엔디언으로 변환!!!
mov sin.sin_port, ax                        ; 워드크기임을 주의 하기 바란다. AX
invoke inet_addr, addr IPAddress     ; 빅엔디언으로 변환!!!
mov sin_addr, eax
invoke connect, socket, addr sin, sizeof sin
.if eax == SOCKET_ERROR
    invoke WSAGetLastError             ; 실제에러코드 검출
    .if eax != WSAEWOULDBLOCK     ; WSAEWOULDBLOCK은 에러가 아니므로!
        < 에러처리 루틴 >
    .endif
.endif        


만약에 IP주소가 아니라, "http://www.openserver.co.kr/my"등의 URL로 얻었다면,  
먼저 호스트이름을 추출해내야 한다. (xx.com / cc.co.kr...) 그리고 얻어낸 호스트
이름을 gethostbyname 프로시져를 호출해서 변환해야 한다.

  gethostbyname PROTO lphostname:DWORD

lphostname : 호스트이름을 포함하는 문자열의 포인터

성공적으로 수행되면, hostent 구조체의 포인터를 반환 하게 된다. 만약 NULL을 반환 하면
WSAGetLastError 프로시져로 에러코드를 검출하면 된다. hostent 구조체는 다음과 같은 구조로 되어있다.

   hostentStru STRUCT
     h_name  DWORD ?
     h_alias DWORD ?
     h_addr  WORD  ?
     h_len   WORD  ?
     h_list  WORD  ?
   hostentStru ENDS

   MASM32패키지를 이용하고 있다면, windows.inc파일에 hostentStru 라는 이름으로 되어있다.

   h_name     : 호스트의 공식이름
   h_alias      : 호스트이름의 다른 별명 - NULL문자로 종료되어야 한다!
   h_addr       : 타입. 윈속에서는 항상 AF_INET 이다.
   h_len        : 타입의 길이. AF_INET의 경우는 4 이다.
   h_list        : IP주소 리스트의 포인터. (이중포인터이다)

이 프로시져는 호스트명에서 IP주소로 변경하기 위해서 사용한다. h_list를 위해서 주로 사용.

코드샘플

.data
sin SOCKADDR_IN <>
hostname db "openserver.co.kr", 0
Port dd 80

.data?
socket dd ?

.code
....
mov sin.sin_family, AF_INET
invoke htons, Port
mov sin.sin_port, ax
invoke gethostbyame, addr hostname
mov eax, [eax+12]                            ; h_list멤버를 eax에 대입
mov eax, [eax]                                ; 실제IP주소를 가리키는 포인터를 대입
mov eax, [eax]                                ; IP주소를 대입
mov sin.sin_addr, eax
invoke connect,socket, addr sin, sizeof sin
.if eax == SOCKET_ERROR                ; 넌블러킹모드 사용시 항상 SOCKET_ERROR를 반환!!
    invoke WSAGetLastError
    .if eax != WSAEWOULDBLOCK
        < 에러처리 루틴 >
    .endif
.endif



5. 데이터 전송 / 수신 작업
send 프로시져로 데이터를 스트림형태로 보내고, sendto 프로시져로 데이터그램 형태로 데이터를 전송
할 수 있다. 여기서는 대부분 많이 사용하는 스트림형태인 send 에 대해서 알아보도록 하자.

send PROTO socket:DWORD, buffer:DWORD, len:DWORD, flags:DWORD

socket    : 소켓설명자
buffer     : 보내고자 하는 데이터의 포인터. 데이터는 NULL종료문자로 되어있지 않다.
len        : 보내고자하는 데이터의 크기
flags      : 프로시져의 행동을 지정한다. 두가지의 행동이 정의 되어있다.
               MSG_DONTROUTE : 라우트되지 않도록 권고한다. 단지 권고일뿐다.
               MSG_OOB       : 범람데이터 (긴급메시지)
                  대부분의 경우 flags는 사용하지 않으므로, "0"으로 설정한다.

실패하면 SOCKET_ERROR를 반환하게 된다.(eax) 성공적으로 수행되면 eax레지스터에 실제로 전송한
바이트수를 반환한다. 이수치는 len 에서 설정한 크기와 동일하지는 않을 수 있다. 
단어 의미 그대로 원격호스트로 데이터를 전송한다!

코드샘플

.data?
buffer db 100 dup(?)
....
.code
invoke send, socket, buffer, 100, 0
.if eax == SOCKET_ERROR
    < 에러 처리 루틴 >
.else
 ....
.endif

이제, 이와는 반대로 데이터를 수신하는 것에 대해서 알아보도록 하자.
윈속에서는 recv 와 recvfrom이라는 두가지 프로시져가 존재한다. send와 동일하게 recv는
TCP 서비스인 스트림이고 recvfrom은 UDP서비스인 데이터그램을 수신한다.

   recv PROTO socket:DWORD, buffer:DWORD, len:DWORD, flags:DWORD

socket   : 소켓설명자
buffer     : 수신되는 데이터를 저장할 메모리 블럭의 주소
len        : 수신 데이터 보관용 블럭의 크기
flags      : 이 또한 두가지가 존재한다.
               MSG_PEEK : 수신되는 데이터를 가져온다. 가져온 데이터는 입력큐에서 복사됨
               MSG_OOB  : 범람데이터 처리. FD_OOB이벤트가 발생할 경우만 사용함

성공적으로 수행되면 소켓으로 부터 읽어들인 바이트 수를 반환한다. 실패하면 SOCKET_ERROR를 반환
한다. 만약 반환값이 "0" 이라면 원격호스트의 소켓이 닫힌것이다.

이 프로시져는 소켓으로 부터 데이터를 읽어들이고 메모리의 버퍼영역에 데이터를 저장 한다. 소켓으로
부터 얼마나 많은 데이터를 읽어들일 수 있을까? 이것은 ioctlsocket 프로시져를 통해서 알 수 있다.
다음과 같은 프로토타입을 가진다.

   ioctlsocket PROTO socket:DWORD, cmd:DWORD, lpArgument:DWORD

socket          : 소켓 설명자
cmd             : 소켓명령을 의미하며, 3가지가 존재한다.
                     FIONBIO, FIONREAD, SIOCATMARK이다. 소켓에서 처리가능한 크기를 얻고자할때,
                     FIONREAD를 살펴보면 된다. FIONREAD 명령은 소켓에서 읽을수 있는 크기를
                     설정하는 것이다. lpArgument 에 가용크기가 반환된다.
lpArgument   : 상기의 cmd에서 부가적으로 사용한다

성공적으로 수행되면, EAX레지스터에 NULL을 반환한다. 실패하게 되면 여느때와같이 SOCKET_ERROR가
반환되며, WSAGetLastError로 실제 에러코드를 검출하면 된다.

이 프로시져는 FIONBIO 명령으로 블러킹/넌블러킹 모드를 조절할 수있으며, FIONREAD명령 으로 소켓에서
처리가능한 크기를 설정할 수 있으며, SIOCATMARK명령으로  수신데이터중 범람데이터(긴급메세지)의
여부를 알 수 있게 된다.

코드샘플

.data?
socket               dd ?
hMemory              dd ?        ; 메모리블럭 핸들
buffer               dd ?            ; 메모리블럭 포인터
available_data       dd ?        ; 소켓 가용데이터 크기
actual_data_read     dd ?      ; 실제 소켓 수신가용 크기
....
.code
....
invoke ioctlsocket,socket, FIONREAD, addr available_data
.if eax == NULL
    invoke GlobalAlloc, GHWN, available_data
    mov hMemory, eax
    invoke GlobalLick, eax
    mov buffer, eax
    invoke recv, socket, buffer, available_data, 0
    mov actual_data_read, eax
    ....
    < 버퍼 데이터 사용 >
    ....
    invoke GlobalUnlock, buffer
    invoke GlobalFree, hMemory
.endif


6. Socket 접속해제
소켓의 사용이 끝났다면 반드시 closesocket 프로시져로 종료해주어야 한다.

  closesocket PROTO socket:DWORD

socket : 소켓설명자

성공적으로 수행되면 NULL을 반환하며, 실패하면 여느때와 같다. (SOCKET_ERROR)

코드샘플

.data?
socket dd ?
....
.code
....
invoke closesocket, socket
.if eax == SOCKET_ERROR
    < 에러처리 루틴 >
.else
endif


7. winsok.dll 해제
이제 소켓도 다 사용했고, 프로그램에서 winsock.dll이 필요없으므로 해제 해야한다.
이작업은 WSACleanup 프로시져가 담당한다.

   WSACleanup PROTO

이 프로시져는 인수가 없다. (있을 이유가 없다)
성공적으로 해제하면 NULL을 반환하고, 에러가 발생하면 SOCKET_ERROR를 반환한다.

참고로, WSAStartup 과 WSACleanup 프로시져는 쌍으로 작동되야 한다. 예를 들어서
WSAStartup을 세번 호출했다면 WSACleanup 또한 3번 수행해야한다.


코드샘플

.code
....
invoke WSACleanup
.if eax == SOCKET_ERROR
    < 에러 처리 루틴 >
.else
....
.endif



::: 마치며 :::

이상으로 간단(?)하게 나마 윈속프로그래밍을 어셈블리에서 어찌할 지에 대해서 알아보았다. 실제에
어찌 적용되는 지는 다음의 채팅예제를 통해서 이해하기 바란다. 
DevX환경에서 Chatting.wap(프로젝트) 를 열어서 Shift + F8을 눌러서 실행하기 바란다.
또는 Chatting.exe파일을 바로 실행해도 된다.  "파일" -> "접속" 에서 자신의 시스템(혹은 원격)에
접속해서 상단 에디트박스에 입력하면 하단 에디트박스에 출력이 되는 간단한 프로그램이다.

예제는 아래의 링크를 클릭해서 다운로드 받길 바란다. 소켓은 다 이런식이다. 데이터를 받아서
어떻게 표시해줄지는 여러분의 재량에 달려있을뿐이고, 소켓처리엔진은 이런식이라는 것만 알아두기
바라며, 자신의 프로그램을 네트워크에 연결되게 하길 바라며..........

> DevX Chatting Example Download <

윈속프로그래밍은 일정한 룰을 지키면서 각종 에러처리를 해주는 것이 대부분이다. 실제 연결은 그다지
어렵지 않다.  이러한 윈속프로그래밍은 비단 어셈블리에서만 가능한것이 아님은 누구나 알것이다.
다른 언어에서도 같은 방식으로 사용한다.

Posted by openserver