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
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:

  1. Initialize winsock dll
  2. Create socket
  3. Specify the operation mode: blocking or non-blocking
  4. Connect the socket
  5. Perform some tasks with the socket such as sending or receiving data
  6. Close the socket
  7. 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: 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
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:


Socket Creation
After the successful WSAStartup call, you can proceed to create the socket. Parameters
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:


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. 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. 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


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: 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: namelen == the size of SOCKADDR_IN structure

Return Value
Depend on the mode of the socket.

Code Snippet 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: Parameters
lphostname == pointer to the host name string.

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.

Note that in windows.inc supplied by hutch, hostent structure is named hostentStru. 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

.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. 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: 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

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. 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: 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:

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


Closing the Socket
After you are finished with using the socket, you should close it by calling closesocket. 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


Release Winsock Library
When the application doesn't need to use winsock library anymore, it calls WSACleanup. 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


[Iczelion's Winsock Section][Iczelion's Win32 Assembly Homepage]