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