To get started on internet programing, let's try to bounce a packet of a distant machine. True, this is not as hopelessly techno-romantic as moon bouncing, but I would think there would be a thrill of accomplishment as you take your first trip on the information highway.
The following pair of programs are a server-client pair. The server program runs on one machine listening for a request packet from the client program and responds back to the client with response packet. The client program runs on another machine (although it could run on the same machine), sending a request packet to the server and listening for the response packet. This pattern of request-reply initiated by the client and responded to by the server is the basis for client-server computer network interactions.
Your gateway to the information highway is the the socket, more properly the Berkeley Socket. A socket is a software abstraction that summarizes all of the networking machinery into a file handle. The socket handle is very similar to a standard file handle in that it supports the actions of reading and writing. But the API supports a few special actions, e.g. to set the destination address, or to sense the source address.
UDP sockets can be bound or unbound, and connected or unconnected.
The bind call assigns a port number to the socket and from that moment packets to the port from any host are put on the socket's receive queue. Recvfrom must be called on a bound socket, and sendto, if called on an unbound socket will bind it to an default interface at an ephemeral port. A UDP socket sends and receives on the same port. Once bound, both the destination and the source ports will be the bound port number.
If the socket becomes connected, it is associated with a specific source host and port and the queue will be restricted to packets from that port on that host. Connecting only occurs if a connect call occurs to the socket.
Connected UDP sockets are curious things. Only a connected socket can receive ICMP packets (see this faq). Once connected using the connect call, the usage of sendto and recvfrom is restricted — at best the address structure will be ignored. There seems to be a critical race between arriving packets during the interval between bind and connect which I have not found discussed in the documentation.
We will not use connected UDP sockets. The establishment of a session, which is the effect of socket connection, will be handled by our own software and protocols.
The Server
/*
** netbounce-server.c
** from Beej's Guide to Network Programming: Using Unix Sockets,
** by Brian "Beej" Hall.
**
** modified by Burt Rosenberg,
** Created: Feb 8, 2009
** Last modified: Jan 31, 2015 - added comment about mild
** bug if received data is binary -bjr
** Jan 12, 2016 - add getopt
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#define MAXBUFLEN 100
#define USAGE_MSG "usage: %s [-lv] -p port\n"
#define PROG_NAME "netbounce-server"
int main(int argc, char * argv[]) {
int sockfd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
unsigned int addr_len, numbytes;
char buf[MAXBUFLEN];
int ch ;
int is_verbose = 0 ;
int port = 0 ;
int is_loop = 0 ;
while ((ch = getopt(argc, argv, "vp:l")) != -1) {
switch(ch) {
case 'p':
port = atoi(optarg) ;
break ;
case 'v':
is_verbose = 1 ;
break ;
case 'l':
is_loop = 1 ;
break ;
default:
printf(USAGE_MSG, PROG_NAME) ;
return 0 ;
}
}
argc -= optind;
argv += optind;
if ( !port ) {
printf(USAGE_MSG, PROG_NAME) ;
return 0 ;
}
assert(port) ;
if ((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1) {
perror("socket") ;
exit(1) ;
}
my_addr.sin_family = AF_INET ;
my_addr.sin_port = htons((short)port) ;
my_addr.sin_addr.s_addr = INADDR_ANY ;
memset(&(my_addr.sin_zero),'\0',8) ;
if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1 ) {
perror("bind") ;
exit(1) ;
}
while (1==1 ) { /* while forever */
addr_len = sizeof(struct sockaddr) ;
if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN-1,0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
// assume can be a string, and terminate
buf[numbytes] = '\0' ;
if ( is_verbose ) {
/* added to get source port number */
printf("got packet from %s, port %d\n", inet_ntoa(their_addr.sin_addr),
ntohs(their_addr.sin_port)) ;
printf("packet is %d bytes long\n", numbytes ) ;
/* Mild bug: if the incoming data was binary, the "string" might be less than numbytes long */
printf("packet contains \"%s\"\n", buf ) ;
}
{
/* return the packet to sender */
int bytes_sent ;
if ((bytes_sent = sendto( sockfd, buf, strlen(buf), 0,
(struct sockaddr *)&their_addr, sizeof(struct sockaddr)))==-1 ) {
perror("send") ;
exit(1) ;
}
if ( is_verbose ) {
printf("packet sent, %d bytes\n", bytes_sent) ;
}
}
if ( !is_loop ) break ;
}
close(sockfd) ;
return 0 ;
}
The Client
/*
** netbounce-client.c
** from Beej's Guide to Network Programming: Using Unix Sockets,
** by Brian "Beej" Hall.
**
** modified by Burt Rosenberg,
** Created: Feb 8, 2009
** Last modified: Jan 31, 2015 - minor changes -bjr
** Jan 12, 2015 - added getopt
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<assert.h>
#define MAXBUFLEN 100
#define USAGE_MESSAGE "usage: %s [-v] -p port -h host message\n"
#define PROG_NAME "netbounce-client"
int main( int argc, char * argv[] ) {
int sockfd ;
struct sockaddr_in their_addr ;
struct hostent *he ;
int numbytes ;
int port = 0 ;
char * host = NULL ;
char * msg = NULL ;
int ch ;
int is_verbose = 0 ;
while ((ch = getopt(argc, argv, "vp:h:")) != -1) {
switch(ch) {
case 'p':
port = atoi(optarg) ;
break ;
case 'h':
host = strdup(optarg) ;
break ;
case 'v':
is_verbose = 1 ;
break ;
default:
printf(USAGE_MESSAGE, PROG_NAME) ;
return 0 ;
}
}
argc -= optind;
argv += optind;
if ( !host || !port || ! argc ) {
printf(USAGE_MESSAGE, PROG_NAME) ;
return 0 ;
}
msg = strdup(*argv) ;
/* example of an assertion */
assert(host) ;
assert(port) ;
if ((he=gethostbyname(host))==NULL) {
perror("gethostbyname") ;
exit(1) ;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) {
perror("socket") ;
exit(1) ;
}
their_addr.sin_family = AF_INET ;
their_addr.sin_port = htons((short)port) ;
their_addr.sin_addr = *((struct in_addr *)he->h_addr) ;
memset(&(their_addr.sin_zero), '\0', 8 ) ;
if ((numbytes=sendto(sockfd, msg, strlen(msg),0,
(struct sockaddr *)&their_addr, sizeof(struct sockaddr)) ) == -1 ) {
perror("sendto") ;
exit(1) ;
}
if (is_verbose) {
printf("send %d bytes to %s\n", numbytes, inet_ntoa(their_addr.sin_addr)) ;
}
{
// if you must introduce new variables, make a block
struct sockaddr_in my_addr ;
unsigned int addr_len ;
char buf[MAXBUFLEN];
addr_len = sizeof(struct sockaddr_in) ;
getsockname( sockfd, (struct sockaddr *)&my_addr, &addr_len ) ;
if ( is_verbose ) {
printf("sent from port %d\n", ntohs(my_addr.sin_port)) ;
printf("calling for return packet\n") ;
}
addr_len = sizeof(struct sockaddr_in) ;
if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN-1,0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
if ( is_verbose ) {
printf("got packet from %s, port %d\n", inet_ntoa(their_addr.sin_addr),
ntohs(their_addr.sin_port)) ;
printf("packet is %d bytes long\n", numbytes ) ;
}
buf[numbytes] = '\0' ;
printf("%s\n", buf ) ;
}
close(sockfd) ;
return 0 ;
}
The Makefile
#
# makefile for netbounce example
# Jan 12,2016
# bjr
#
CC = gcc
LOCALHOST = localhost
PORT = 3333
all:
make netbounce
netbounce: netbounce-server netbounce-client
netbounce-server: netbounce-server.c
${CC} -o $@ $<
netbounce-client: netbounce-client.c
${CC} -o $@ $<
test:
@echo In one window, make run-server
@echo Then, in another window, make run-client
run-server: netbounce-server
@echo Ready to bounce!
./netbounce-server -lv -p ${PORT}
run-client: netbounce-client
./netbounce-client -h ${LOCALHOST} -p ${PORT} -v "The amazing net bounce!"
submit:
svn commit -m "submitted for grade"
clean:
-rm netbounce-client netbounce-server
Author: Burton Rosenberg
Created: January 2013
Last Update: January 31, 2020