Anda di halaman 1dari 19

Iczelion's Guide to Winsock Programming

Winsock or windows socket takes the concept from Unix. The idea behind sockets is to use them as communication
devices between different machines in the network. Each machine can create one or more sockets to connect to
other machines. Sockets make uses of TCP/IP protocol as such, it operates in the higher network layers. We use
sockets in any higher protocol that wants to use TCP/IP as its transport media such as HTTP,FTP and so on. The
socket itself doesn't understand or care about the content that passes through it. It just does its job of sending and
receiving data to from its remote counterpart.
Types of Socket
There are two types of socket: stream socket and datagram socket. A stream socket makes use of TCP so its
transmission requires connection and is reliable. You use a stream socket when you want to send large files over the
net or you want sequenced data packets (that is, the order of data packets is important). A datagram socket
provides simple transfer of data. It's not guaranteed to be reliable (the data may not reach its destination),
sequenced (the data packets may arrive in different order than intended), or unduplicated (the destination socket
may receive two or more identical copies of the same data packets). HTTP and FTP and some other protocols use
stream sockets while some broadcast protocols use datagram sockets.
You can think of stream socket connection as a telephone chat. You first have to make a call and if the call is
successful, a connection is established. Then you can exchange information with the other end in a reliable way. If
some communication problem occurs you know of it immediately and can take measures to rectify it. Datagram
socket connection is like sending several mails to someone. You cannot know beforehand that they will reach the
intended person. Even if he received them, there's no guarantee that the mails will reach him in the order you sent.
And the mails may not reach the target at all.
The socket model sometimes uses client-server concept that is, one socket is thought of as server since it has
services that it can render for other sockets, the opposite end is the client which asks the server for some services.
HTTP and FTP make uses of this client-server concept.
Byte Ordering
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Since we must deal with IP addresses in winsock programming, we should know of different byte ordering first.
There are two types of byte ordering: big Endian and little Endian. In big Endian scheme, the leftmost byte are stored
in the most significant byte. Little Endian scheme is the reverse of big Endian. For example, if the IP address is
205.34.67.24, in big Endian scheme it will be 205 34 67 24. But in little Endian, it will be 24 67 34 205.
Intel CPU uses little endian while some other CPUs such as Motorola uses big endian. The final word is: the internet
uses big endian scheme so if you use a CPU with little endian byte ordering, you must convert the IP addresses
before using them with the net.
Blocking and non-blocking modes
Socket functions can operate in two modes: blocking and non-blocking. Originally, in Berkeley Unix implementation,
the sockets operate in blocking mode, that is, a socket function will not return until the operation is completed. We
can call "blocking mode" synchronous operation and "non-blocking mode" asynchronous operation. Blocking
operation may take an arbitrarily long time to complete such as waiting for the data to arrive from the remote socket.
During that time, the program will seem frozen. This is usually unacceptable in Windows environment. So Windows
implementation of socket API includes asynchronous (non-blocking) versions of the original blocking functions. You
should use non-blocking versions whenever possible since it conforms to Windows paradigm and provides better
results than the blocking ones.
Ports
When you create a socket and connect it to the remote socket, you must specify the port that the sockets will
communicate with each other. Ports in this case is not hardware ports like COM1 or COM2. Ports in winsock
programming are virtual ones for communication purpose only. Maybe an example will make this clear. Say, the
server creates a socket and instructs it to listen for incoming connection on port 21, although there may be many
network packets coming into the server, those packets that are destined for port number 21 will be routed to that
socket. Several Internet protocols has their own default ports. HTTP uses port 80 (decimal) and FTP uses port 21
(decimal). These are default ports only. It means if both client and server agree to communicate via different ports,
they can do so without any repercussion.
Winsock Programming Overview
Programming winsock normally includes these steps:
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

1.
2.
3.
4.
5.
6.
7.

Initialize winsock dll


Create socket
Specify the operation mode: blocking or non-blocking
Connect the socket
Perform some tasks with the socket such as sending or receiving data
Close the socket
Release winsock dll

We will explore these steps in some detail.

Socket Library Initialization


You must call WSAStartup to initialize the winsock dll before using any winsock function. This function has the
following syntax:
WSAStartup PROTO wVersionRequired:DWORD, LPWSADATA:DWORD
Parameters
wVersionRequried == The winsock version your application wants to use. At this time, version 1.1 and 2 are
available. Version 1.1 ships with Windows 95 and version 2.x comes with Windows NT 4.0. If you want to specify
version 1.1, use 101h if you want to use version 2.0, use 200h
lpWSADATA == Pointer to WSADATA structure that the winsock dll will fill in. This structure contains implementation
details of the current winsock dll. Normally, you just create an uninitialized instance of WSADATA and pass its
address to WSAStartup.
Return Value
WSAStartup returns NULL if the call is successful. Otherwise it returns error code. You can look up the error codes in
winsock.hlp. This is the only winsock function that returns the actual error code because you cannot call
WSAGetLastError if the socket dll is not initialized first.
Comment
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

If this function is not successful, you cannot use any winsock function. In fact, this function also serves as a
negotiation routine for the best version of winsock api service for the application. After the call is successful,
WSADATA structure will be filled with the capabilities of the current winsock implementation. One of the members is
the highest version of winsock that the library supports. Say, if the application requests for at least winsock version
1.1 support and the current winsock library can support version 2, WSAStartup call will be successful and the
winsock library returns 200h in a member of WSADATA. If the application can make use of winsock 2 support, it
may call WSACleanup to close the previous initialization and call WSAStartup again, this time with 200h in its
wVersionRequired parameter. If your application supports only winsock 1.1, you don't have to examine the
WSADATA structure.
Code Snippet:
.data
wsadata WSADATA <>
.....
.code
.......
invoke WSAStartup, 101h,addr wsadata
.if eax!=NULL
<An error occured>
.else
<The initialization is successful. You may proceed with other winsock calls>
.endif

Socket Creation
After the successful WSAStartup call, you can proceed to create the socket.
socket PROTO af:DWORD, type:DWORD, protocol:DWORD
Parameters
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

af == Address format which at the current time there is only one: PF_INET
type == type of the socket you want to create, stream or datagram one. If you want a stream socket, use
SOCK_STREAM else use SOCK_DGRAM
protocol == if the af param is AF_UNSPEC (unspecified), you must specify the protocol here. However, since we
always use PF_INET, you can use 0.
Return Value
If the call is unsuccessful, the return value is INVALID_SOCKET and you can call WSAGetLastError to retrieve the
actual error code. If the call is successful, it returns the socket descriptor which you must use in subsequent winsock
calls.
Comment:
You must create at least one socket to be used as the communication device from your end. After successful call,
you must save the returned socket descriptor for use with subsequent winsock function calls.
Code Snippet:
.data?
sock dd ?
....
.code
....
invoke socket,AF_INET,SOCK_STREAM,0
.if eax!=INVALID_SOCKET
mov sock,eax
.else
invoke WSAGetLastError
...
.endif
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

; Create a stream socket for internet use

pdfcrowd.com

Specifying Socket Options


After the socket is created, it defaults to blocking mode. If you want it to operate in non-blocking mode, you have to
set that with setsockopt or WSAAsyncSelect. Normally, you should use WSAAsyncSelect to turn on non-blocking
mode for the socket. So we'll demonstrate the use of WSAAsyncSelect here. setsockopt is not hard to use, you
can figure it out yourself. setsockopt can alter many characteristics of a socket so you may want to look at it later.
WSAAsyncSelect PROTO socket:DWORD, hwnd:DWORD, msg:DWORD, Event:DWORD
Parameters
socket == socket descriptor that is returned from socket call.
hwnd == handle to the window that will receive notification of winsock events.
msg == custom windows message that you create and want the winsock library to send to your window in case the
events you're interested in occur.
Event == winsock events your application is interested in. You can specify one of the constant below or add them
together if your application is interested in more than one winsock event.
FD_READ Want to receive notification of readiness for reading
FD_WRITE Want to receive notification of readiness for writing
FD_OOB Want to receive notification of the arrival of out-of-band data
FD_ACCEPT Want to receive notification of incoming connections
FD_CONNECT Want to receive notification of completed connection
FD_CLOSE Want to receive notification of socket closure
Out-of-band data deserves some explanation here. Out-of-band data is the data that are sent without regard to
the normal order of the general data. You can think of out-of-band data as an emergency notification as such it
must be distinguished from the normal data. There are two ways to receive out-of-band data: in-line and
separate channel. Normally, winsock library will put out-of-band data in a separate channel by default and will
notify the application when out-of-band data arrive. However, some application may want to receive out-ofband data in the same channel as the normal data. In this case, that application must call setsockopt to
modify SO_OOBINLINE option and call ioctlsocket to check if there are some out-of-band data in the socket.
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Return Value
If the call is successful, the return value is NULL. Otherwise it returns SOCKET_ERROR and you may call
WSAGetLastError to retrieve the actual error code.
Comment
This function is the instrument of non-blocking paradigm. It lets an application specify which winsock events it's
interested in and when the registered winsock events occur, the winsock library will send the specified message to
the window. Contrast this to the blocking scenario in which you must poll for the winsock events. This function also
changes the socket to non-blocking mode. I recommend that you use WSAAsyncSelect whenever applicable since
it conforms to Windows paradigm.
If you don't want the winsock library to send notifications to your window anymore, you must call WSAAsyncSelect
with value 0 in lEvent parameter. However, beware that there may be winsock messages in the queue before you
cancel winsock notification. You must be prepared to handle them. Normally you should extract them from the
message queue by PeekMessage call with PM_REMOVE flag.
When a winsock event that the application is interested occurs, the message specified by msg parameter will be
sent to the window procedure of the window specified by hwnd parameter. With the message, wParam contains the
socket descriptor, high word of lParam contains error code (if any) and the low word of lParam contains the event.
Code Snippet
.data?
hwnd dd ?
; handle of the window to receive the winsock message.
socket dd ?
; socket descriptor
....
.const
WM_SOCKET equ WM_USER+100 ; create a custom window message. You can use any name not
; limited to WM_SOCKET and you can use any number not
; limited to 100.
......
.code
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

...........
invoke WSAAsyncSelect, socket, hwnd,WM_SOCKET, FD_CONNECT+FD_READ+FD_CLOSE
; Register interest in connect, read and close events.
.if eax==SOCKET_ERROR
<put your error handling routine here>
.else
........
.endif
WndProc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
.......
.if uMsg==WM_SOCKET
; the message we specified in WSAAsyncSelect
mov eax,lParam
.if ax==FD_CONNECT
; the low word of lParam contains the event code.
shr eax,16
; the error code (if any) is in the high word of lParam
.if ax==NULL
<no error occurs so proceed>
.else
<an error occurs. Put your error handling routine here>
.endif
.elseif ax==FD_READ
shr eax,16
.if ax==NULL
<no error occurs so proceed>
.else
<an error occurs. Put your error handling routine here>
.endif
.elseif ax==FD_CLOSE
shr eax,16
.if ax==NULL
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

<no error occurs so proceed>


.else
<an error occurs. Put your error handling routine here>
.endif
.endif
.else
..........
.endif
WndProc ENDP

Connection to a Remote Socket


After the socket is created, you have two choices: wait for an incoming connection or connect to a remote socket. If
you want to wait for incoming connection, you must call listen to listen for an incoming connection and call accept to
establish connection with the remote socket. If you want to connect to a remote socket, you must call connect which
has the following syntax:
connect PROTO socket:DWORD, lpSockAddr_in:DWORD, namelen:DWORD
Parameters
socket == socket descriptor of the local socket. You can pass the socket descriptor returned by socket call as this
parameter.
lpSockAddr_in == a pointer to a SOCKADDR_IN structure which is declared as follows:

sockaddr_in STRUCT
sin_family WORD
?
sin_port WORD
?
sin_addr DWORD
?
sin_zero BYTE 8 dup (?)
sockaddr_in ENDS
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

You must fill in this structure and pass its address to connect.
sin_family is the same as af parameter in socket call. You must use AF_INET here.
sin_port is the port that the socket will use to communicate with the remote socket. This value depends
on the higher level protocol that you want to use. If you want to connect to the remote socket for HTTP,
use port 80 (decimal). However, note that the port value MUST be in network byte order that is big
Endian. So you cannot use the port value per se but you must call htons to convert the value to network
byte order first. This is one of the most common error the newcomers to winsock programming
encounter.
sin_addr is the IP address of the remote host. Again, you must convert the IP address to network byte
order before using it.
sin_zero is reserved. Don't mess with it.
namelen == the size of SOCKADDR_IN structure
Return Value
Depend on the mode of the socket.
non-blocking mode: it returns SOCKET_ERROR and you must call WSAGetLastError to retrieve the error
code. If the error code is WSAEWOULDBLOCK ,it means that the operation is in progress and cannot return
immediately. The application will be informed of the result later by custom window message specified in
WSAAsyncSelect call. In a nutshell, WSAEWOULDBLOCK is not an error.
blocking mode: The return value indicates the success or failure of the call.
Code Snippet
.data
sin SOCKADDR_IN <>
IPAddress db "206.34.234.23",0
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Port dd 80

; We use port 80, HTTP port for demonstration purpose

.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; convert port number into network byte order first
mov sin.sin_port,ax
; note that this member is a word-size param.
invoke inet_addr, addr IPAddress ; convert the IP address into network byte order
mov sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; assuming we use non-blocking mode, connect
; will always return SOCKET_ERROR
invoke WSAGetLastError
; retrieve the actual error code
.if eax!=WSAEWOULDBLOCK
; if it's not WSAEWOULDBLOCK
<put your error handling code here>
; it means a true error occurred
.endif
.endif
If you only got URL string such as "http://members.xoom.com/Iczel", you must parse the string for the host name, in
this case "members.xoom.com". And pass the address of the host name as a parameter to gethostbyname
function which has the following syntax:
gethostbyname PROTO lphostname:DWORD
Parameters
lphostname == pointer to the host name string.
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Return Value
If successful, it returns a pointer to hostent structure which is allocated by Windows sockets implementation. If the
call fails, it returns NULL and you may call WSAGetLastError to retrieve the error code.
hostentStru STRUCT
h_name
DWORD
?
h_alias DWORD
?
h_addr WORD
?
h_len
WORD
?
h_list DWORD ?
hostentStru ENDS
Note that in windows.inc supplied by hutch, hostent structure is named hostentStru.
h_name Official name of the host (PC).
h_alias A NULL-terminated array of alternate names.
h_addr The type of address being returned; for Windows Sockets this is always PF_INET.
h_len The length, in bytes, of each address; for PF_INET, this is always 4.
h_list Pointer to a pointer that points to the list of ip addresses. In summary, h_list is a double pointer
indirection. Addresses are returned in network byte order so you can use them immediately.
Comment
This function is used to retrieve the IP address(es) of the host specified as a string. Notice that what you want mostly
is in h_list member.
Code Snippet
.data
sin SOCKADDR_IN <>
hostname db "members.xoom.com",0
Port dd 80
; We use port 80, HTTP port for demonstration purpose
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

.data?
socket dd ?
.code
........
mov sin.sin_family, AF_INET
invoke htons, Port
; convert port number into network byte order first
mov sin.sin_port,ax
; note that this member is a word-size param.
invoke gethostbyname, addr hostname
mov eax,[eax+12]
; move the value of h_list member into eax
mov eax,[eax]
; copy the pointer to the actual IP address into eax
mov eax,[eax]
; copy IP address into eax
mov sin.sin_addr,eax
invoke connect,socket,addr sin,sizeof sin
.if eax==SOCKET_ERROR
; assuming we use non-blocking mode, connect
; will always return SOCKET_ERROR
invoke WSAGetLastError
; retrieve the actual error code
.if eax!=WSAEWOULDBLOCK
; if it's not WSAEWOULDBLOCK
<put your error handling code here>
; it means a true error occurred
.endif
.endif

Operations on the Socket


There are several operations you can perform on the socket such as sending or receiving data. We'll examine both
of them in detail.

Sending Data to the Remote Socket


We use send to send data on a stream socket and sendto to send data on a datagram socket. I'll examine send
here since many popular protocols such as HTTP and FTP use stream sockets.
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

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


Parameters
socket == socket descriptor
buffer == address of the data that you want to send. The data need not be NULL-terminated since its size is
specified in len parameter.
len == the size of data to send
flags == flags specifying the behavior of the function. There are two flags you can use:
MSG_DONTROUTE : specifies that the data should not be subject to routing. Notice the word "should". Some
windows socket implementation may ignore this flag.
MSG_OOB : specifies that the data is out-of-band data.
Normally we don't use any of these two flags, flags parameter should be 0 in this case.
Return Value
If the call is unsuccessful, the value SOCKET_ERROR is returned in eax. If it's successful, the actual number of
bytes sent is returned in eax. Note that this number may not be equal to len parameter.
Comment
This function sends data to the connected remote socket. It neither cares nor knows about the data it sends.
Code Snippet
.data?
buffer db 100 dup(?)
....
.code
.....
invoke send, socket, buffer, 100, 0
.if eax==SOCKET_ERROR
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

<put your error handling code here>


.else
.....
.endif

Reading Data from the Socket


There are two variants of the winsock call to read data from the socket: recv and recvfrom. recv is for use with a
stream socket while recvfrom is for a datagram socket.
recv PROTO socket:DWORD, buffer:DWORD, len:DWORD, flags:DWORD
Parameters
socket == socket descriptor
buffer == address of the memory block to store the incoming data.
len == the size of the memory block
flags == flags specifying the behavior of the function. There are two flags you can use:
MSG_PEEK Peek at the incoming data. The data is copied into the buffer but is not removed from the input
queue.
MSG_OOB Process out-of-band data. This flag is usually used when FD_OOB notification is received.
Return Value
If the call is successful, it returns the number of bytes read from the socket. If it is unsuccessful, the value
SOCKET_ERROR is returned. If the return value is 0, the remote socket has been closed.
Comment
This function reads the data from the socket and stores them in the specified buffer.A question arises: how do you
know how many bytes are available for read from the socket? The answer is the function ioctlsocket. It has the
following syntax:
ioctlsocket PROTO socket:DWORD, cmd:DWROD, lpArgument:DWORD
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

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


Parameters
socket == socket descriptor
cmd == the command to perform on socket. There are three commands: FIONBIO, FIONREAD, and
SIOCATMARK. Since we want to get the size of data available in the socket, we will focus on FIONREAD.
FIONREAD command determines the amount of data which can be read from the socket. The amount of data is
stored at the location pointed to by lpArgument.
lpArgument == address of an additional parameter for cmd.
Return Value
If successful, the return value in eax is NULL. Otherwise the value SOCKET_ERROR is returned and you may call
WSAGetLastError to retrieve the error code.
Comment
This function is used to control the mode of a socket. With FIONBIO command, it can switch the socket into blocking
or non-blocking mode. With FIONREAD command, it returns the amount of data available in the socket. With
SIOCATMARK command, it checks whether or not all out-of-band data has been read.
Code Snippet
.data?
socket dd ?
hMemory dd ?
; handle to memory block
buffer dd ?
; address of the memory block
available_data dd ?
; the amount of data available from the socket
actual_data_read dd ? ; the actual amount of data read from the socket
.....
.code
.....
invoke ioctlsocket, socket, FIONREAD, addr available_data
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

.if eax==NULL
invoke GlobalAlloc, GHND, available_data
mov hMemory,eax
invoke GlobalLock, eax
mov buffer,eax
invoke recv, socket, buffer, available_data, 0
mov actual_data_read, eax
.....
<use the data in buffer>
....
invoke GlobalUnlock, buffer
invoke GlobalFree, hMemory
.endif

Closing the Socket


After you are finished with using the socket, you should close it by calling closesocket.
closesocket PROTO socket:DWORD
Parameter
socket == socket descriptor
Return Value
If successful, it returns NULL. Otherwise SOCKET_ERROR is returned and you may call WSAGetLastError to
retrieve the error code.
Comment
This function closes a socket. Every resource of the socket will be released.
Code Snippet
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

.data?
socket dd ?
....
.code
....
invoke closesocket, socket
.if eax==SOCKET_ERROR
<put your error handling code here>
.else
endif

Release Winsock Library


When the application doesn't need to use winsock library anymore, it calls WSACleanup.
WSACleanup PROTO
Parameter
This function takes no parameter.
Return Value
If successful, it returns NULL. Otherwise it returns SOCKET_ERROR.
Comment
There must be matching number of WSAStartup and WSACleanup calls. Say, if you call WSAStartup three times,
you must also call WSACleanup three times as well else the winsock library will not be able to free any unused
resource. This is the last function that you call when you're through with the winsock library.
Code Snippet
open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

.code
.....
invoke WSACleanup
.if eax==SOCKET_ERROR
<put your error handling code here>
.else
.endif
[Iczelion's Winsock Section][Iczelion's Win32 Assembly Homepage]

open in browser PRO version

Are you a developer? Try out the HTML to PDF API

pdfcrowd.com

Anda mungkin juga menyukai