What do we have in this chapter?
Winsock Tutorial
Winsock: Story 1Winsock: Story 2Winsock: Story 3Winsock: Example 1Winsock: Example 2Winsock: Example 3Winsock: Example 4Winsock: Example 5Winsock: Example 6Winsock: Example 7Winsock: Example 8Winsock: Example 9Winsock: Example 10Winsock: Example 11Winsock: Example 12Winsock: Reference 1Winsock: Reference 2Winsock: Reference 3Winsock: Reference 4Another Complete and Advanced C & Winsock2 Programming Tutorial
|
My Training Period: hours
Machine’s OS is standalone Windows XP Pro with SP2 except whenever mentioned. Compiler used was Visual C++ 2003 .Net 1.1. Beware the codes that span more than one line. Program examples have been tested for Non Destructive Test. All information compiled for Windows 2000 (NT5.0) above and...
Abilities:
The IPv6 Client Program Example
And the client program for IPv6 example.
|
LPSTR DecodeError(int ErrorCode)
{
static char Message[1024];
// If this program was multi-threaded, we'd want to use
// FORMAT_MESSAGE_ALLOCATE_BUFFER instead of a static buffer here.
// (And of course, free the buffer when we were done with it)
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_MAX_WIDTH_MASK,
NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)Message, 1024, NULL);
return Message;
}
int ReceiveAndPrint(SOCKET ConnSocket, char *Buffer, int BufLen)
{
int AmountRead;
AmountRead = recv(ConnSocket, Buffer, BufLen, 0);
if (AmountRead == SOCKET_ERROR)
{
fprintf(stderr, "Client: recv() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
closesocket(ConnSocket);
WSACleanup();
exit(1);
}
else
printf("Client: recv() is OK.\n");
// We are not likely to see this with UDP, since there is no 'connection' established.
if (AmountRead == 0)
{
printf("Client: Server closed the connection...\n");
closesocket(ConnSocket);
WSACleanup();
exit(0);
}
printf("Client: Received %d bytes from server: \"%.*s\"\n", AmountRead, AmountRead, Buffer);
return AmountRead;
}
int main(int argc, char **argv)
{
char Buffer[BUFFER_SIZE], AddrName[NI_MAXHOST];
char *Server = DEFAULT_SERVER;
int Family = DEFAULT_FAMILY;
int SocketType = DEFAULT_SOCKTYPE;
char *Port = DEFAULT_PORT;
int i, RetVal, AddrLen, AmountToSend;
int ExtraBytes = DEFAULT_EXTRA;
unsigned int Iteration, MaxIterations = 1;
BOOL RunForever = FALSE;
WSADATA wsaData;
ADDRINFO Hints, *AddrInfo, *AI;
SOCKET ConnSocket;
struct sockaddr_storage Addr;
printf("Usage: %s [-s server] [-f family] [-t transport] [-p port] [-b bytes] [-n number]\n", argv[0]);
printf("Example: %s -s 127.0.0.1 -f PF_INET6 -t TCP -p 1234 -b 1024 -n 4\n", argv[0]);
printf("Else, default values used.\n\n");
if (argc > 1)
{
for (i = 1;i < argc; i++)
{
if (((argv[i][0] == '-') || (argv[i][0] == '/')) &&
(argv[i][1] != 0) && (argv[i][2] == 0))
{
switch(tolower(argv[i][1]))
{
case 'f':
if (!argv[i+1])
Usage(argv[0]);
if (!stricmp(argv[i+1], "PF_INET"))
Family = PF_INET;
else if (!stricmp(argv[i+1], "PF_INET6"))
Family = PF_INET6;
else if (!stricmp(argv[i+1], "PF_UNSPEC"))
Family = PF_UNSPEC;
else
Usage(argv[0]);
i++;
break;
case 't':
if (!argv[i+1])
Usage(argv[0]);
if (!stricmp(argv[i+1], "TCP"))
SocketType = SOCK_STREAM;
else if (!stricmp(argv[i+1], "UDP"))
SocketType = SOCK_DGRAM;
else
Usage(argv[0]);
i++;
break;
case 's':
if (argv[i+1])
{
if (argv[i+1][0] != '-')
{
Server = argv[++i];
break;
}
}
Usage(argv[0]);
break;
case 'p':
if (argv[i+1])
{
if (argv[i+1][0] != '-')
{
Port = argv[++i];
break;
}
}
Usage(argv[0]);
break;
case 'b':
if (argv[i+1])
{
if (argv[i+1][0] != '-')
{
ExtraBytes = atoi(argv[++i]);
if (ExtraBytes > sizeof(Buffer) - sizeof("Message #4294967295"))
Usage(argv[0]);
break;
}
}
Usage(argv[0]);
break;
case 'n':
if (argv[i+1])
{
if (argv[i+1][0] != '-')
{
MaxIterations = atoi(argv[++i]);
break;
}
}
RunForever = TRUE;
break;
default:
Usage(argv[0]);
break;
}
}
else
Usage(argv[0]);
}
}
// Request for Winsock version 2.2.
if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
{
fprintf(stderr, "Client: WSAStartup() failed with error %d: %s\n", RetVal, DecodeError(RetVal));
WSACleanup();
return -1;
}
else
printf("Client: WSAStartup() is OK.\n");
// By not setting the AI_PASSIVE flag in the hints to getaddrinfo, we're
// indicating that we intend to use the resulting address(es) to connect
// to a service. This means that when the Server parameter is NULL,
// getaddrinfo will return one entry per allowed protocol family
// containing the loopback address for that family.
memset(&Hints, 0, sizeof(Hints));
Hints.ai_family = Family;
Hints.ai_socktype = SocketType;
RetVal = getaddrinfo(Server, Port, &Hints, &AddrInfo);
if (RetVal != 0)
{
fprintf(stderr, "Client: Cannot resolve address [%s] and port [%s], error %d: %s\n", Server, Port, RetVal, gai_strerror(RetVal));
WSACleanup();
return -1;
}
else
printf("Client: getaddrinfo() is OK, name resolved.\n");
// Try each address getaddrinfo returned, until we find one to which
// we can successfully connect.
for (AI = AddrInfo; AI != NULL; AI = AI->ai_next)
{
// Open a socket with the correct address family for this address.
ConnSocket = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
if (ConnSocket == INVALID_SOCKET)
{
fprintf(stderr,"Client: Error Opening socket, error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
continue;
}
else
printf("Client: socket() is OK.\n");
// Notice that nothing in this code is specific to whether we
// are using UDP or TCP.
//
// When connect() is called on a datagram socket, it does not
// actually establish the connection as a stream (TCP) socket
// would. Instead, TCP/IP establishes the remote half of the
// (LocalIPAddress, LocalPort, RemoteIP, RemotePort) mapping.
// This enables us to use send() and recv() on datagram sockets,
// instead of recvfrom() and sendto().
printf("Client: Attempting to connect to: %s\n", Server ? Server : "localhost");
if (connect(ConnSocket, AI->ai_addr, int(AI->ai_addrlen)) != SOCKET_ERROR)
break;
else
printf("Client: connect() is OK.\n");
i = WSAGetLastError();
if (getnameinfo(AI->ai_addr, int(AI->ai_addrlen), AddrName, sizeof(AddrName), NULL, 0, NI_NUMERICHOST) != 0)
strcpy(AddrName, "<unknown>");
fprintf(stderr, "Client: connect() to %s failed with error %d: %s\n", AddrName, i, DecodeError(i));
closesocket(ConnSocket);
}
if (AI == NULL)
{
fprintf(stderr, "Client: Fatal error: unable to connect to the server.\n");
WSACleanup();
return -1;
}
// This demonstrates how to determine to where a socket is connected.
AddrLen = sizeof(Addr);
if (getpeername(ConnSocket, (LPSOCKADDR)&Addr, &AddrLen) == SOCKET_ERROR)
{
fprintf(stderr, "Client: getpeername() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
}
else
{
if (getnameinfo((LPSOCKADDR)&Addr, AddrLen, AddrName, sizeof(AddrName), NULL, 0, NI_NUMERICHOST) != 0)
strcpy(AddrName, "<unknown>");
printf("Client: Connected to %s, port %d, protocol %s, protocol family %s\n", AddrName, ntohs(SS_PORT(&Addr)), (AI->ai_socktype == SOCK_STREAM) ? "TCP" : "UDP", (AI->ai_family == PF_INET) ? "PF_INET" : "PF_INET6");
}
// We are done with the address info chain, so we can free it.
freeaddrinfo(AddrInfo);
// Find out what local address and port the system picked for us.
AddrLen = sizeof(Addr);
if (getsockname(ConnSocket, (LPSOCKADDR)&Addr, &AddrLen) == SOCKET_ERROR)
{
fprintf(stderr, "Client: getsockname() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
}
else
{
printf("Client: getsockname() is OK.\n");
if (getnameinfo((LPSOCKADDR)&Addr, AddrLen, AddrName, sizeof(AddrName), NULL, 0, NI_NUMERICHOST) != 0)
strcpy(AddrName, "<unknown>");
printf("Client: Using local address %s, port %d\n", AddrName, ntohs(SS_PORT(&Addr)));
}
// Send and receive in a loop for the requested number of iterations.
for (Iteration = 0; RunForever || Iteration < MaxIterations; Iteration++)
{
// compose a message to send.
AmountToSend = sprintf(Buffer, "This is message #%u", Iteration + 1);
for (i = 0; i < ExtraBytes; i++)
{
Buffer[AmountToSend++] = (char)((i & 0x3f) + 0x20);
}
// Send the message. Since we are using a blocking socket, this
// call shouldn't return until it's able to send the entire amount.
RetVal = send(ConnSocket, Buffer, AmountToSend, 0);
if (RetVal == SOCKET_ERROR)
{
fprintf(stderr, "Client: send() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError()));
WSACleanup();
return -1;
}
else
printf("Client: send() is OK.\n");
printf("Client: Sent %d bytes (out of %d bytes) of data: \"%.*s\"\n", RetVal, AmountToSend, AmountToSend, Buffer);
// Clear buffer just to prove we're really receiving something.
memset(Buffer, 0, sizeof(Buffer));
// Receive and print server's reply.
ReceiveAndPrint(ConnSocket, Buffer, sizeof(Buffer));
}
// Tell system we're done sending.
printf("Client: Sending done...\n");
shutdown(ConnSocket, SD_SEND);
// Since TCP does not preserve message boundaries, there may still
// be more data arriving from the server. So we continue to receive
// data until the server closes the connection.
if (SocketType == SOCK_STREAM)
while (ReceiveAndPrint(ConnSocket, Buffer, sizeof(Buffer)) != 0)
;
closesocket(ConnSocket);
WSACleanup();
return 0;
}
Testing the IPv6 Client-server Programs
Run the previous server program example, then run the client program. The following is the client program’s output.
C:\>myclient -s 127.0.0.1 -f PF_INET -t TCP -p 12345 -b 32 -n 4
Usage: myclient [-s server] [-f family] [-t transport] [-p port] [-b bytes] [-n number]
Example: myclient -s 127.0.0.1 -f PF_INET6 -t TCP -p 1234 -b 1024 -n 4
Else, default values used.
Client: WSAStartup() is OK.
Client: getaddrinfo() is OK, name resolved.
Client: socket() is OK.
Client: Attempting to connect to: 127.0.0.1
Client: Connected to 127.0.0.1, port 12345, protocol TCP, protocol family PF_INET
Client: getsockname() is OK.
Client: Using local address 127.0.0.1, port 1032
Client: send() is OK.
Client: Sent 50 bytes (out of 50 bytes) of data: "This is message #1 !"#$%&'()*+,-./0123456789:;<=>?"
Client: recv() is OK.
Client: Received 50 bytes from server: "This is message #1 !"#$%&'()*+,-./0123456789:;<=>?"
Client: send() is OK.
Client: Sent 50 bytes (out of 50 bytes) of data: "This is message #2 !"#$%&'()*+,-./0123456789:;<=>?"
Client: recv() is OK.
Client: Received 50 bytes from server: "This is message #2 !"#$%&'()*+,-./0123456789:;<=>?"
Client: send() is OK.
Client: Sent 50 bytes (out of 50 bytes) of data: "This is message #3 !"#$%&'()*+,-./0123456789:;<=>?"
Client: recv() is OK.
Client: Received 50 bytes from server: "This is message #3 !"#$%&'()*+,-./0123456789:;<=>?"
Client: send() is OK.
Client: Sent 50 bytes (out of 50 bytes) of data: "This is message #4 !"#$%&'()*+,-./0123456789:;<=>?"
Client: recv() is OK.
Client: Received 50 bytes from server: "This is message #4 !"#$%&'()*+,-./0123456789:;<=>?"
Client: Sending done...
Client: recv() is OK.
Client: Server closed the connection...
C:\>
Finally the previous server output.
C:\>mywinsock -f
Usage: mywinsock [-f family] [-t transport] [-p port] [-a address]
Example: mywinsock -f PF_INET6 -t TCP -p 1234 -a 127.0.0.1
Else, default values used. By the way, the -a not usable for server...
Ctrl + C to terminate the server program.
Simple socket server program.
mywinsock [-f family] [-t transport] [-p port] [-a address]
family One of PF_INET, PF_INET6 or PF_UNSPEC. (default PF_UNSPEC)
transport Either TCP or UDP. (default: TCP)
port Port on which to bind. (default 2007)
address IP address on which to bind. (default: unspecified address)
C:\>mywinsock -f PF_INET -t TCP -p 12345
Usage: mywinsock [-f family] [-t transport] [-p port] [-a address]
Example: mywinsock -f PF_INET6 -t TCP -p 1234 -a 127.0.0.1
Else, default values used. By the way, the -a not usable for server...
Ctrl + C to terminate the server program.
Server: WSAStartup() is OK.
Server: getaddrinfo() is OK.
Server: socket() is OK.
Server: bind() is OK.
Server: listen() is OK.
I'm listening and waiting on port 12345, protocol TCP, protocol family PF_INET
Server: select() is OK.
Server: accept() is OK.
Accepted connection from 127.0.0.1.
Server: recv() is OK.
Server: Received 50 bytes from client: "This is message #1 !"#$%&'()*+,-./0123456789:;<=>?"
Server: Echoing the same data back to client...
Server: send() is OK.
Server: recv() is OK.
Server: Received 50 bytes from client: "This is message #2 !"#$%&'()*+,-./0123456789:;<=>?"
Server: Echoing the same data back to client...
Server: send() is OK.
Server: recv() is OK.
Server: Received 50 bytes from client: "This is message #3 !"#$%&'()*+,-./0123456789:;<=>?"
Server: Echoing the same data back to client...
Server: send() is OK.
Server: recv() is OK.
Server: Received 50 bytes from client: "This is message #4 !"#$%&'()*+,-./0123456789:;<=>?"
Server: Echoing the same data back to client...
Server: send() is OK.
Server: recv() is OK.
Server: Client closed the connection.
^C
C:\>
Further reading and digging:
Linux Sockets: Story and program examples.
Structure, enum, union and typedef story can be found struct, enum, union & typedef tutorial.
For Multibytes, Unicode characters and Localization please refer to Multibyte, Unicode and wide characters (Story) and Win32 Windows & Users tutorial (Implementation).
A complete info on Windows socket reference from MSDN which include managed and unmanaged API doc.
Windows data type information is Win32 - Windows data types.
Check the best selling C / C++ and Windows books at Amazon.com.