Lesson 4:
A Winsock UDP sender
[Fig. 4-1, a UDP packet being
sent from the sender to the receiver]
Introduction
The UDP
protocol is somewhat different from the TCP protocol, the most important
difference is that it’s message-based, that is you don’t establish any
“connections” to the receiver, you only send messages. The second difference is
that UDP is an unreliable protocol,
unlike TCP, UDP doesn’t guarantee that your information reaches its
destination. You can compare UDP with the postal service, you aren’t guaranteed
that the mail will reach its destination (although it often does). TCP however
can be compared with a phone call, you exchange information simultaneously and you are guaranteed that the information is delivered.
Obtaining command-line arguments and initialization
You
probably know this part of the code by now, but I’ll show it anyway, for
completeness sake:
//
author: frenchwhale
(http://frenchwhales_site.tripod.com)
#include <winsock.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MESSAGE "This
is an UDP message." int main(int argc, int **argv) { WSADATA
wsda; // Structure to store info //
returned from WSAStartup struct
hostent *host; // Used to store
information //
retreived about the server char
szMessage[80]; int
iMessageLen; int ret; char
szAddress[64]; int iPort; SOCKET s; // Our TCP socket handle SOCKADDR_IN
addr; // The host's address // Check
arguments if(argc !=
3 || (argc==2
&& strcmp((char *) &argv[1][0], "/?")==0)) {
printf("wsudpsnd server port\n");
printf(" server: the server
to send the packet to\n"); printf(" port:
the port on the remote server\n"); exit(1); } // Copy the
IP address
strcpy(szAddress, (char *) &argv[1][0]); // Get the
remote port iPort =
atoi((char *) &argv[2][0]);
if(iPort<0 || iPort>65563) {
printf("Invalid port number! (%s)\n", argv[2]); exit(1); } // Copy the
pre-defined message into a buffer
strcpy(szMessage, MESSAGE); iMessageLen
= strlen(szMessage); // Load
version 1.1 of Winsock
WSAStartup(MAKEWORD(1,1), &wsda); |
The code obtains the command-line arguments, checks if the port number is valid and initializes Winsock, if you aren’t familiar with these concepts, see Lesson 2.
Socket creation and resolving
Here
comes the first major difference between the TCP client and the UDP sender, the
socket creation:
// Create a UDP socket
printf("Creating socket...");
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// Error?
if(s == SOCKET_ERROR)
{
printf("Error\nCall to socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
failed with:\n%d\n",
WSAGetLastError());
exit(1);
}
printf("OK\n");
// Fill in the host information
addr.sin_family = AF_INET;
addr.sin_port = htons(iPort);
addr.sin_addr.s_addr = inet_addr(szAddress);
if(addr.sin_addr.s_addr == INADDR_NONE) //
The address wasn't in numeric //
form, resolve it
{
host = NULL;
printf("Resolving host...");
host = gethostbyname(szAddress); //
Get the IP address of the server //
and store it in host
if(host == NULL)
{
printf("Error\nUnknown host: %s\n", szAddress);
exit(1);
}
memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
printf("OK\n");
} |
The code creates a socket of the AF_INET family (for the TCP/IP protocol), the SOCK_DGRAM type (for a datagram-based socket) and IPPROTO_UDP protocol (tells Winsock to use the UDP protocol). The different family/type/protocol combinations can be found in Lesson 2, Fig 2-1. The reason for using IPPROTO_UDP is because it’s the only protocol you can use with AF_INET and SOCK_DGRAM. The resolving is done the same way as in the TCP client, you assume that the address is in numeric form (aaa.bbb.ccc.ddd) and if it doesn’t work, it resolves it using the resolving techniques learned in Lesson 2.
Sending and cleanup
Since a
UDP socket is never connected to the receiver (you can
connect it, but it’s unusual), you
have to tell Winsock the address of the receiver in another way, this is done
with the sendto function, which is declared as:
int sendto(SOCKET s, const
char FAR *buf, int len, int flags, const struct sockaddr FAR *to, int tolen);
The
first argument to sendto is the UDP socket you’re sending on, the
second is a pointer to a buffer holding the data you want to send in the
packet, the third is the length of the data, the fourth is some flags (just set
it to 0), the fifth is a pointer to a sockaddr structure representing the
address of the receiver and finally, the last argument is the length of the sockaddr structure.
When you use this function, you use the first four arguments as you would using send, the fifth and sixth arguments however are set to the address of the receiver and the address structure length.
Here’s the last part of the code:
// Ready to send data
printf("Sending packet...");
ret = sendto(s, szMessage, iMessageLen, 0, (struct sockaddr *)
&addr, sizeof(addr));
if(ret == SOCKET_ERROR)
{
printf("Error\nCall to sendto(s, szMessage, iMessageLen, 0,
(SOCKADDR_IN *) &addr, sizeof(addr)); failed with:\n%d\n",
WSAGetLastError());
exit(1);
}
printf("OK\n");
printf("\"%s\" sent to %s\n", szMessage, szAddress);
closesocket(s);
WSACleanup();
return 0; } |
The
packet is sent, an error check is made, the socket is closed and WSACleanup is called.
Source code for Lesson 4:
n WSUDPSND.C – Sends a UDP packet to a host containing a pre-specified message.