1. Initialize Windows Sockets
The following initialization code is found early in the
program's main function.
WSABUF WSABuf = {0};
WSAStartup(WINSOCK_VERSION, &WSAData);
Initializing Windows Sockets is critical. Failing to initialize
Windows Sockets will cause all other socket function calls to
fail.
This sample utilizes Windows Sockets version 2.2. Therefore, the
macro WINSOCK_VERSION is defined in the system header
file WinSock2.h. An application that calls
WSAStartup must call WSACleanup when it
is done using the Windows Socket services (see step 11).
2. Create a local socket
The call to create a socket looks like this:
SOCKET Socket = WSASocket(AF_INET, Type, Protocol,
NULL, 0, 0);
Where Type is SOCK_STREAM and
Protocol is IPPROTO_TCP for a
blocking, connection oriented TCP/IP socket.
3. Bind to a local port
To bind the socket to a local port, call the following
function:
bind(Socket, &SockAddr, sizeof(SockAddr));
Binding to a local port must be done before using the socket to
calls like WSAConnect, and is used to associate the
currently unnamed socket with a local name.
The SockAddr parameter is the SOCKADDR
representation of the local name to bind to. This name consists of
an address family (for TCP/IP, always AF_INET), a host
address, and a port number.
In this sample, the calls to WSASocket and
bind are both done in the function
MySocketOpen. Since this sample demonstrates a
server application, a well-known port (7777) is
supplied to the call to MySocketOpen. The same
well-known port is also used by the device server as part of its
port AutoConnect TCP/IP address information.
4. Listen for incoming connections
Start listening for incoming connection requests by calling the
listen function.
listen(ListenSocket, 1);
Calling listen puts the socket into a relatively
inactive mode where it waits for incoming connection requests from
a client. Connection requests are queued until processed in a
"backlog". The size of this backlog is specified in the second
parameter to the call to listen.
5. Wait to accept an incoming connection
Call WSAAccept to wait for an incoming
connection request.
SOCKET ConnectedSocket = WSAAccept(ListenSocket,
&AcceptSockAddr,
&AcceptSockAddrLength,
NULL, 0);
The sample is using blocking sockets, so the call to
WSAAccept will not return until there is at least one
connection request pending in the backlog queue. The socket value
returned from WSAAccept represents the new connection
that was just established. The listening socket remains in the
passive listening mode, and the new connected socket should be used
to communicate with the client.
6. Initiate an AutoConnect
Attach the serial loopback plug to port 1 on the
device server.
Attaching and detaching the serial loopback plug causes the DCD
signal to be raised or lowered (respectively). Since the port was
configured to AutoConnect when carrier is detected (DCD high),
attaching the loopback plug initiates an AutoConnect from the
device server to the sample application.
7. Transmit data
Transmitting data is done by calling
WSASend.
WSASend(Socket, &WSABuf, 1, Length, 0, NULL, NULL);
After accepting the incoming connection, the sample uses the
socket to transmit some data.
To simplify transmitting data, and to hide many of the details
associated with the parameters necessary to call
WSASend, this sample uses its own function named
MySocketSend.
MySocketSend(ConnectedSocket, SendData, &SendLength);
The only parameters needed for MySocketSend are:
MySocket which is the socket returned from
MySocketOpen, SendData which is a
pointer to the buffer holding the data to send, and
SendLength which is the number of bytes
SendData points to.
8. Receive data
To retrieve data received at the local address, call
WSARecv.
WSARecv(Socket, &WSABuf, 1, Length, &Flags,
NULL, NULL);
With the device server setup with the loopback plug, the data
transmitted in the step above will be sent directly back to the
sample application.
Like WSASend, WSARecv has a large
number of parameters, providing great flexibility at the price of
adding to its complexity. Also like WSASend, the
sample provides an alternative function,
MySocketRecv that is somewhat easier to
use.
MySocketRecv(ConnectedSocket, RecvData, &BytesReceived);
The caller to MySocketRecv need only supply the
prerequisite MySocket, a buffer
RecvData to hold the received data, and
BytesReceived which specifies the length of
RecvData and on return exactly how many bytes were
actually copied into the buffer.
9. Close the connection
Detach the serial loopback plug to port 1 on the
device server.
This causes DCD to be lowered and the device server to properly
close the TCP session it has to the sample application.
10. Close the connected socket
Accomplish this task by calling the following two functions:
shutdown(Socket, SD_BOTH);
closesocket(Socket);
When the sample application is done using the socket, the
connection it represents must be terminated properly
(shutdown) and any resources it may be using should be
released (closesocket).
11. Cleanup
Before exiting the program, call:
WSACleanup();
For every successful call to WSAStartup an
application completes, the application must make one call to
WSACleanup.