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:
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:
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:
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
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
<no error occurs so proceed>
.else
<an error occurs. Put your error handling routine here>
.endif
.endif
.else
..........
.endif
WndProc ENDP
You must fill in this structure and pass its address
to connect.
Return Value
Depend on the mode of the socket.
.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
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.
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
Normally we don't use any of these two flags, flags parameter should be 0 in this case.
Comment
This function sends data to the connected remote
socket. It neither cares nor knows about the data it sends.
Code Snippet
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:
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
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
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