User Datagram Protocol Exercise

Protocols Overview

The data link layer can provide reliable and unreliable data transfer between the network layer of hosts. Several protocols based on datagrams are discussed and implemented using C by Tannenbaum though the underlying physical layer services are not implemented, required for actual experimentation. The simpler C protocol algorithms (Protocol 1-4) were implemented in Java using datagrams as the underlying protocol. As many of you noted, few if any errors were observed on the LAN, making the unreliable datagram in fact reasonably reliable. Even Protocol 2, which could deadlock on errors rarely would given the low error rate typical of LANs. As an example of a familiar program, Protocol 1 has been implemented below using datagrams directly. The key elements of Java for inserting datagrams onto and receiving datagrams from the network are discussed below.

Python Datagram Services

A key advantage of Python for network applications is the relative simplicity of communication using datagram and connection services. The following discusses the key parts of Java for implementing datagram service.

Port - A workstation is uniquely identified by its IP number such as 149.160.51.113 associated with an NIC. Even though only one NIC is used the host may be communicating with multiple sites simultaneously. To maintain each communication separately, datagrams are sent and received through unique (to the hostports. A few applications, such as SMTP (port 25), HTTP (port 80), FTP, etc. have well-known port numbers that are generally reserved. In our example, we used one port to send (sendPort=999) and another to receive (receivePort=666). Though it is possible to use one bi-directional port, because the sender and receiver are running on the same host, each bi-directional communication must use a different port number, that is only one use of a port number is allowed per computer host. It is important to note that the sender must send to the receiver port number in order for the two to communicate. In the chat implementations below, two different ports are used when the same machine is used for both sides of the chat.

Socket - A datagram socket is somewhat similar to a file handle except that provides a bi-directional access to send or receive datagrams via the network rather than a file system. Under TCP/IP it must be associated with a unique port number for that host. receiverSocket.bind(('',999)) creates a new socket identified with port 999, the object ds can be used to send or receive datagram packets through port 999.

Receiving - Receive on socket ds using port 999 data and address of sender.

ds=socket(AF_INET, SOCK_DGRAM)

 

 ds.bind(('',999))
 
 data, address = ds.recvfrom(1024)

Sending - A new datagram containing a string (e.g. 'Hello world') is sent via socket ds to port 999 of the destination machine  cs1.ius.edu through the network:

ds.sendto('Hello World', ('cs1.ius.edu', 999))

Protocol 1 

Protocol 1 from Tannenbaum has a weakness in that communication is simplex with no coordination between sender and receiver to prevent overflow of the receiver. The following implements Protocol 1 using datagrams. The diagram at right illustrates the port communication.

Sender                                                                               Receiver

# Protocol 1 sender using Python datagrams

 

 
 from socket import *
 
 senderSocket=socket(AF_INET, SOCK_DGRAM)
 
 while True :
    userIn=raw_input()         # from_network
    if not userIn : break        # to_physical
    senderSocket.sendto(userIn, ('localhost', 999))
 senderSocket.close()

# Protocol 1 receiver using Python datagrams

from socket import *

receiverSocket=socket(AF_INET, SOCK_DGRAM)
receiverSocket.bind(('',999))

while True :                   # from_physical
   data, address = receiverSocket.recvfrom(1024)
   if not data : break
   print data                   # to_network
receiverSocket.close()

 


chat0 Python

ipconfig

     ioSocket.sendto(userIn, ('localhost', ioPort))

where localhost is the IP or name of host to chat with.

 

chat0.py

# chat0

from socket import *

ioSocket=socket(AF_INET, SOCK_DGRAM)

ioPort = 11111

receiverPort=ioPort

toName = 'localhost'

if toName == 'localhost' : receiverPort = 22222

try :

      ioSocket.bind(('',ioPort))

except Exception :

    ioPort = 22222

    receiverPort = 11111

    ioSocket.bind(('',ioPort))

print 'chat0 on port ', ioPort, '. Blank line to quit.'

while True :

    userIn = raw_input()

    ioSocket.sendto(userIn, (toName, receiverPort))

    data, address = ioSocket.recvfrom(10240)

    if not data : break

    print data

ioSocket.close()

Problems - Obviously the chatters should not be forced to take turns chatting.
Solution - The reasonable solution is to create separate threads of execution for sending and receiving packets allowing multiple packets to be sent from a single, talkative chatter.


chat1

Improvement over chat0 is that it can receive and send at any time using two separate threads, the main thread for sending and a new thread for the receiving. Python supports lightweight threads that execute pseudo-parallel with the main thread. The main thread will continue to handle user typing, a separate thread will handle receiving and displaying datagrams; both threads operate independently avoiding the lockstep, turn-taking of the chat0. Daemon threads are used because they terminate when the main thread terminates.

ipconfig

c:\python25\python chat1.py

     ioSocket.sendto(userIn, ('localhost', ioPort))

where localhost is the IP or name of host to chat with.

chat1.py

# chat1

from socket import *
import threading

class receiver(threading.Thread) : # Receiver thread
   def __init__(self, socket ):
        threading.Thread.__init__(self)
        self.SOCKET=socket
        self.setDaemon(True) # Daemon threads stop with main thread

   def run(self) :
        while True : # Receive in daemon thread
                 data, address = self.SOCKET.recvfrom(10240)
                 if not data : break
                 print address, ' ', data
        self.SOCKET.close()

ioSocket=socket(AF_INET, SOCK_DGRAM)
ioPort = 11111
receiverPort=ioPort
toName = 'localhost'

if toName == 'localhost' : receiverPort = 22222

try :
     ioSocket.bind(('',ioPort))
except Exception, msg :
    ioPort = 22222
    receiverPort = 11111
    ioSocket.bind(('',ioPort))

print 'chat1 on port ', ioPort, '. Blank line to quit.'

receiver(ioSocket).start() # Create receiver thread and start

while True : # Send in main thread
    userIn = raw_input()
    if not userIn : break
    ioSocket.sendto(userIn, (toName, receiverPort))
ioSocket.close()

 

Problem - While two chatters can chat to one another, assuming that they each knew the others IP address, can a new chatter join the chat group? Unfortunately no since new chatters are not learned and remembered as they make contact. Currently the chatter host must be designated in the Python code (i.e. toName='localhost'). 

Consider that A contacts B and B contacts A, then A and B can chat. But if C contacts A, C can chat with A but A can't chat with C!

Solution - An apparently reasonable solution is to add the IP of any new chatters making contact to a chatters list. Chat packets are then sent to all chatters in the list. If A and B are chatting A maintains a list of: A(B) and B the list of chatters B(A). When C contacts A, the lists are now: A(B,C), B(A), C(A).

Exercise 1

  1. Propose a solution,
  2. implement the solution, the instructor will help.
  3. Name the new program file:
  4. test.
  5. One solution.

 



Problem - While several chatters can now chat to one another, and a new chatter can chat with one of the original chatters, the new chatter does not chat with other chatters in the group. Consider that A contacts B and B contacts A, then A and B can chat. When C contacts A, A adds C to a list of chatters that it will send packets to but A does not send B chat from C or C chat from B. The lists maintained would be: A(B,C), B(A), C(A).

Solution - A apparently reasonable solution is to add the IP of any new chatters making contact to a list and to forward to all chatters in the list any chat received. To avoid loops an exception is made not to forward to the sender of the chat. If A is contacted by B and C, the lists become A(B,C), B(A), C(A). When C chats with A, A forwards any messages to B and will do the same for B chatting with A. A could serve as the contact point for all chatters, effectively acting as a chat server might. Or two chatters that act as servers (e.g. A and C) might contact each and other chatters contact the server (e.g. A contacted by B and C contacted by D). The lists would be: A(B,C), B(A), C(A,D), D(C). The corresponding graph would appear as at right where B chat with A would be forwarded to C which forwards to D.

Two requirements are necessary to avoid loops:

  1. A chatter can contact only one other chatter. For example, C contacts host A when starting execution by: c:\python25\python chat2.py A
  2. Chat is forwarded to all chatters except the one sending the chat. In the graph example, A forwards to all known chatters except B, where B is the original chatter.

Exercise 2

  1. Propose a solution,
  2. implement the solution, the instructor will help.
  3. Name the new program file and class:
  4. test.
  5. One solution. Running two chatters on one host will create a loop and produce infinite output.

 

Java Datagram Services

A key advantage of Java for network applications is the relative simplicity of communication using datagram and connection services. The following discusses the key parts of Java for implementing datagram service.

Port - A workstation is uniquely identified by its IP number such as 149.160.51.113 associated with an NIC. Even though only one NIC is used the host may be communicating with multiple sites simultaneously. To maintain each communication seperately, datagrams are sent and received through unique (to the hostports. A few applications, such as SMTP (port 25), HTTP (port 80), FTP, etc. have well-known port numbers that are generally reserved. In our example, we used one port to send (sendPort=9999) and another to receive (receivePort=6666). Though it is possible to use one bi-directional port, because the sender and receiver are running on the same host, each bi-directional communication must use a different port number, that is only one use of a port number is allowed per computer host. It is important to note that the sender must send to the receiver port number in order for the two to communicate. In the chat implementations below, two different ports are used when the same machine is used for both sides of the chat.

Socket - A datagram socket is somewhat similar to a file handle except that provides a bi-directional access to send or receive datagrams via the network rather than a file system. Under TCP/IP it must be associated with a unique port number for that host. DatagramSocket ds =new DatagramSocket(9999) creates a new socket identified with port 9999, the object ds can be used to send or receive datagram packets through port 999.

Packet - The datagram information is contained in a packet that is sent or received through a socket.

Receiving - Packet to receive must have a byte array for storage of received data and the array length as in:

byte                    buffer = new byte[20];

 

 DatagramSocket ds      = new DatagramSocket(9999);
 DatagramPacket r        = new DatagramPacket( buffer, 20);
 
 ds.receive(r);

defines a packet that can receive a maximum of 20 bytes of data into the array buffer. The datagram r is received through socket ds on port 999 defined above. Note that receive is blocking, that is it waits until a datagram is received before returning.

Sending - A new datagram packet containing the 2 bytes of data (e.g. OK) in buffer of count bytes is sent via socket ds at port number 999 from a machine to port 666 of the destination machine  cs1.ius.edu through the network:

byte                    buffer = { 'O', 'K'};

 

 DatagramSocket ds       = new DatagramSocket(9999);
 
 ds.send(new DatagramPacket( buffer, 2, InetAddress.getByName("cs1.ius.edu" ), 6666));

where buffer is the data to be sent, 2 is the number of bytes of data, 999 is the sender port and 666 is the receive port on host cs1.ius.edu.

InetAddress - The IP address representation of a host.

  • InetAddress(getLocalHost()) returns the address of the host machine executing the statement.
  • InetAddress.getByName("www.sun.com") returns the address of the host http://www.sun.com/.
  • InetAddress.getByName("149.160.23.145") returns the address of the host 149.160.23.145.

Protocol 1 

Protocol 1 from Tannenbaum has a weakness in that communication is simplex with no coordination between sender and receiver to prevent overflow of the receiver. The following implements Protocol 1 using datagrams. The diagram at right illustrates the port communication.

Sender

// Protocol 1 sender using Java datagrams, Tanenbaum

 

 
  import java.net.*;
  import java.io.*;
 
  class p1sender {
    public static void main(String args[]) throws Exception {
 
      byte senderBuffer[] = new byte[128];                   // Packets 128 bytes long
      String toName, userIn;
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      DatagramSocket senderSocket = new DatagramSocket(9999); 
                                                                                 // Create socket for sending
      if (args.length > 0) toName = args[0];                 // IP or DNS from command line
      else                 toName = "localhost";
 
      while ((userIn=in.readLine())!=null ){                   // Read standard input
         senderBuffer=userIn.getBytes();                      // Convert from string to byte array
                                                                                // Send datagram packet out socket
         senderSocket.send(new DatagramPacket( senderBuffer, senderBuffer.length,
                                                 InetAddress.getByName(toName), 6666));
      }       
    }
  }

 

 

 

Receiver

// Protocol 1 receiver using Java datagrams, Tanenbaum 

 

 
  import java.net.*;
 
  class p1receiver {
    public static void main(String args[]) throws Exception {
 
      byte receiveData[] = new byte[128];
 
      DatagramSocket receiveSocket = new DatagramSocket(6666);   
                                                                                          // Create socket for receiving
      DatagramPacket receivePacket = new DatagramPacket(receiveData, 128);
 
      while (true) {
         receiveSocket.receive(receivePacket);                  // Wait for datagram packet to arrive
                                                                                          // Print packet contents as String
         System.out.println(new String(receivePacket.getData(), 0, receivePacket.getLength()));
      }   
    }
  }

where host is the IP or name of host to send the file p1sender.java.

Protocol 1 Sender and Receiver Comparison

// Protocol 1 sender using Java datagrams, Tanenbaum

 

 
 import java.net.*;
 import java.io.*;
 
 class p1sender {
  public static void main(String args[]) throws Exception {
 
   byte senderBuffer[] = new byte[128];  
   String toName, userIn;
   BufferedReader in = new BufferedReader(
                         new InputStreamReader(System.in));
   DatagramSocket senderSocket = new DatagramSocket(9999);
 
   if (args.length > 0) toName = args[0];  
   else                 toName = "localhost";
 
   while ((userIn=in.readLine())!=null ){        
     senderBuffer=userIn.getBytes();     
                                           
     senderSocket.send(
        new DatagramPacket( senderBuffer, senderBuffer.length,
                InetAddress.getByName(toName), 6666));
   }       
  }
 }
// Protocol 1 receiver using Java datagrams, Tanenbaum 

 

 
 import java.net.*;
 
 class p1receiver {
  public static void main(String args[]) throws Exception {
 
    byte receiveData[] = new byte[128];
 
    DatagramSocket receiveSocket = new DatagramSocket(6666);  
    DatagramPacket receivePacket = new DatagramPacket(receiveData, 128);
 
    while (true) {
      receiveSocket.receive(receivePacket);           
                                                          
      System.out.println(
          new String(
              receivePacket.getData(), 0, receivePacket.getLength()));
    }   
  }
 }

simpleChat Java

ipconfig

 java -cp . simpleChat host

where host is the IP or name of host to chat with.

simpleChat.java

import java.net.*;

 

 import java.io.*;
 
 public class simpleChat {
 
    public static void main(String args[]) throws Exception {
    
     DatagramSocket ioSocket;
     String toName, userIn;
     byte sendBuffer[]    = new byte[128];
     byte receiveBuffer[] = new byte[128];
     DatagramPacket sendPacket = new DatagramPacket(sendBuffer, 128);
     DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, 128);
     BufferedReader in = new BufferedReader(
                                             new InputStreamReader( System.in));
 
     int ioPort           = 1111, receiverPort = ioPort;
 
     if (args.length > 0) toName = args[0];  
     else                       toName = "localhost";
     if (toName.equals("localhost"))                        // Use port 1111 or 2222 whether
         receiverPort = 2222;                                     // chatting with localhost or remote
     try { ioSocket = new DatagramSocket(ioPort); }
     catch (Exception e) {                                       // Switch ports when port 1111 in use
         ioPort = 2222; receiverPort = 1111;
         ioSocket = new DatagramSocket(ioPort);
     }
 
     System.out.println("simpleChat on port " + ioPort + ". Ctrl Z to quit.");
 
     while ((userIn=in.readLine())!=null) {                 // Read user input until Ctrl Z
         sendBuffer= userIn.getBytes();                      // Convert string to byte array
         sendPacket = new DatagramPacket( sendBuffer, sendBuffer.length,
                                            InetAddress.getByName(toName), receiverPort);
         ioSocket.send(sendPacket);                        // Send packet
         ioSocket.receive(receivePacket);                // Wait to receive packet
                                                                               // Print contents of received packet
         System.out.println( "Received " + new String(receivePacket.getData(), 
                                                0, receivePacket.getLength()));
     }
    }
 }

Problems - Obviously the chatters should not be forced to take turns chatting.
Solution - The reasonable solution is to create separate threads of execution for sending and receiving packets allowing multiple packets to be sent from a single, talkative chatter.


chat

Improvement over simpleChat is that it can receive and send at any time using two separate threads, the main thread for sending and a new thread for the receiving.

ipconfig

java -cp . chat

java -cp . chat <IP or DNS>

chat.java

import java.net.*;

 

 import java.io.*;
 
 public class chat {
 
    public static void main(String args[]) throws Exception {
    
     DatagramSocket      ioSocket;
     String                      toName         = "localhost";
     String                      userIn;
     byte                        sendBuffer[]  = new byte[128];
     DatagramPacket      sendPacket   = new DatagramPacket(sendBuffer, 128);
     BufferedReader        in                  = new BufferedReader(
                                                              new InputStreamReader(System.in));
     int ioPort                                       = 1111, receiverPort = ioPort;
 
     if (args.length > 0) toName = args[0];  
     else                 toName = "localhost";
     if (toName.equals("localhost"))             // Use two ports for localhost
         receiverPort = 2222; 
     try { ioSocket = new DatagramSocket(ioPort); }
     catch (Exception e) {
         ioPort = 2222; receiverPort = 1111;
         ioSocket = new DatagramSocket(ioPort);
     }
 
     System.out.println("chat on port " + ioPort + ". Ctrl Z to quit.");

 

    new receiver(ioSocket);                          // Start receiver thread

 

    while ((userIn=in.readLine())!=null) {       // Read user input until Ctrl Z
         sendBuffer = userIn.getBytes();          
         sendPacket = new DatagramPacket( sendBuffer, sendBuffer.length,
                                     InetAddress.getByName(toName), receiverPort);
         ioSocket.send(sendPacket);            // Send datagram
     }
     System.exit(0);                                      // Exit, stopping receiver
    }
 }

 

class receiver implements Runnable {

 

 
     DatagramSocket ioSocket;
 
     receiver(DatagramSocket ioSocket) {
       this.ioSocket = ioSocket;
       new Thread(this).start();                      // Start thread at run()
     }
 
     public void run() {
       byte receiveBuffer[] = new byte[128];
       DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, 128);
 
       while(true) {                              

 

        try { ioSocket.receive(receivePacket); }// Wait for datagram

 

         catch (Exception e) {}
         System.out.println( "Received " + new String(receivePacket.getData(), 
                                                 0, receivePacket.getLength()));

 

      }
     }
 }

 

Problem - While two chatters can chat to one another, assuming that they each knew the others IP address, can a new chatter join the chat group? Unfortunately no since new chatters are not learned and remembered as they make contact. Currently only the chatter host designated on the command line is contacted (i.e. java -cp . 149.160.24.23 ). 

Consider that A contacts B and B contacts A, then A and B can chat. But if C contacts A, C can chat with A but A can't chat with C!

Solution

 

chatpeer1

 



Problem - While several chatters can now chat to one another, and a new chatter can chat with one of the original chatters, the new chatter does not chat with other chatters in the group. Consider that A contacts B and B contacts A, then A and B can chat. When C contacts A, A adds C to a list of chatters that it will send packets to but A does not send B chat from C or C chat from B. The lists maintained would be: A(B,C), B(A), C(A).

Solution - A apparently reasonable solution is to add the IP of any new chatters making contact to a list and to forward to all chatters in the list any chat received. To avoid loops an exception is made not to forward to the sender of the chat. If A is contacted by B and C, the lists become A(B,C), B(A), C(A). When C chats with A, A forwards any messages to B and will do the same for B chatting with A. A could serve as the contact point for all chatters, effectively acting as a chat server might. Or two chatters that act as servers (e.g. A and C) might contact each and other chatters contact the server (e.g. A contacted by B and C contacted by D). The lists would be: A(B,C), B(A), C(A,D), D(C). The corresponding graph would appear as at right where B chat with A would be forwarded to C which forwards to D.

Two requirements are necessary to avoid loops:

  1. A chatter can contact only one other chatter. For example, C contacts host A when starting execution by: java -cp . chatpeer2 A
  2. Chat is forwarded to all chatters except the one sending the chat. In the graph example, A forwards to all known chatters except B, where B is the original chatter.

Exercise 2

  1. Propose a solution,
  2. implement the solution, the instructor will help.
  3. Name the new program file and class:
  4. test.
  5. One solution - chatpeer2.txt. Running two chatters on one host will create a loop and produce infinite output.

 


C++ Datagram Services on Windows

Most of the Java ideas transfer in some form to C, whether using Windows, Unix, or other operating system. However, changes are needed to adapt to the specific operating system, this only discusses Windows. The relevant points to running a UDP application such as the following simpleChat are:

Returns the name of the local host.

Returns a hostent data structure pointer constructed for the host name parameter.

Initializes the sockaddr_in data structure used for referring to the receiver.

Initializes the sockaddr_in data structure used for communiating by local host.

Constructs a UDP socket.

Binds the socket ioSocket to the socket address of local.

Send buffer to receiver using ioSocket.

Receive buffer using ioSocket, receiver holds the source address. This is a blocking call, it waits for a datagram before returning.

Port - The concept of a port is common on all TCP/IP implementations whether Java or C. In our example, we used one port to send (ioPort) and receive on the local host. If both sides of the chat are on the same machine receivePort is a different number.

Socket - ioSocket = socket(AF_INET, SOCK_DGRAM,0); creates a new socket unidentified with any port. The varible ioSocket can be used to send or receive datagram packets after being bound to a port.

Binding - bind(ioSocket,(struct sockaddr*)&local,sizeof(local) ); binds ioSocket to the port defined in data structure local. The socket can now be used for sending and receiving datagrams.

Packet - The datagram information is contained in a packet that is sent or received through a socket.

Receiving - Receive buffer using ioSocket, receiver holds the source address. This is a blocking call, it waits for a datagram before returning.

recvfrom(ioSocket,buffer,sizeof(buffer),0, (struct sockaddr *)&receiver,&receiverlen);

Sending - Send buffer to receiver using ioSocket. A new datagram packet containing the data in buffer is sent from the local machine via socket ioSocket through the network to the receiver by:

sendto(ioSocket,buffer,strlen(buffer)+1,0, (struct sockaddr *)&receiver,receiverlen);

simpleChat C++

·       .NET and Visual Studio

Visual Studio 6.0 requires adding the following to the project: C:\Program Files\Microsoft Visual Studio\VC98\Lib\ws2_32.lib

Visual C++ .NET requires adding the following to the project: C:\Program Files\Microsoft Visual Studio .NET\Vc7\PlatformSDK\lib\ws2_32.lib

simpleChat.cpp

//      simpleChat.cpp - Use: simpleChat <IP> or <DNS>
//      Visual C++ Project | Settings | Link | Object/Library modules | ws2_32.lib

#include "stdafx.h"
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <iostream.h>

int main(int argc, char* argv[])
{
  char                         buffer[128];
  unsigned short          ioPort = 111, receiverPort = ioPort;
  int                            retval, receiverlen=128;
  unsigned int             addr=0;
  struct sockaddr_in    local, receiver;
  struct hostent          *host;
  WSADATA                wsaData;
  SOCKET                   ioSocket;
  char                         localName[128];

  WSAStartup(0x202,&wsaData);                     // Startup

  gethostname(localName, sizeof(localName));      // Check if chatting with self
  host = gethostbyname(localName);
  strcpy(localName, host->h_name);
  host = gethostbyname(argv[1]);
  if ((host && strcmp(localName, host->h_name) == 0) || 
               strcmp(argv[1], "127.0.0.1") == 0)
          receiverPort = 222;                     // Chatting with self, use two ports

  if (!host) addr = inet_addr(argv[1]);           // IP used instead of a name?

  if ((!host)  && (addr == INADDR_NONE) ) {       // Failed to resolve name or IP
      cout << "Unable to resolve " << argv[1] << '\n';
      return -1;
  }
  if (host != NULL) {                             // Copy receiver info
      memcpy(&(receiver.sin_addr),host->h_addr,host->h_length);
      receiver.sin_family = host->h_addrtype;
  }
  else {
      receiver.sin_addr.s_addr = addr;
      receiver.sin_family = AF_INET;
  }
  receiver.sin_port = htons(receiverPort);

  local.sin_family = AF_INET;
  local.sin_addr.s_addr = INADDR_ANY;
  local.sin_port = htons(ioPort);

  ioSocket = socket(AF_INET, SOCK_DGRAM,0);

  if (ioSocket == INVALID_SOCKET){
     cout << "socket() failed with error " << WSAGetLastError() << '\n';
     return -1;
  }

  if (bind(ioSocket,(struct sockaddr*)&local,sizeof(local) ) == SOCKET_ERROR) {
      unsigned short temp = ioPort;        // Try switching ports for chatting with self
      ioPort = receiverPort;
      receiverPort = temp;
      local.sin_port = htons(ioPort);
      receiver.sin_port = htons(receiverPort);
      if (bind(ioSocket,(struct sockaddr*)&local,sizeof(local) ) == SOCKET_ERROR) {
         cout << "bind() failed with error " << WSAGetLastError() << '\n';
         return -1;
      }
  }

  cout << argv[0] << " listening on " << ioPort << " protocol UDP\n";
  receiverlen = sizeof(receiver);

  while(cin >> buffer) {

     retval = sendto(ioSocket,buffer,strlen(buffer)+1,0,
                    (struct sockaddr *)&receiver,receiverlen);

     if (retval == SOCKET_ERROR)
        cout << "sendto() failed with error " << WSAGetLastError() << '\n';

     retval = recvfrom(ioSocket,buffer,sizeof(buffer),0,
                      (struct sockaddr *)&receiver,&receiverlen);

     cout << "Received " << buffer << "\n";
  }
}


VB Datagram Services

VB UDP is through the WinSock object described in Homework 6. A workstation is uniquely identified by its IP number such as 149.160.51.113 associated with an NIC. Though only one NIC is used the host may be communicating with multiple sites simultaneously. To maintain each communication seperately, datagrams are sent and received through unique (to the hostports. A few applications, such as SMTP (port 25), HTTP (port 80), FTP, etc. have well-known port numbers that are generally reserved. In our example, we used one port to send (sendPort) and another to receive (receivePort), even though it is possible to use one bi-directional port. It is important to note that the sender port number on one host must match the receiver port number on the other host in order for the machines to communicate. In the chat implementations below, two different ports are used when the same machine is used for both sides of the chat.

The key points and examples, assuming that the name of the WinSock object is WinSock1 are:

Protocol   Winsock1.Protocol = sckUDPProtocol - Defines the protocol as UDP.

IP or DNS of remote host- WinSock1.RemoteHost = "localhost" - Defines the name of the remote host.

Local Port - WinSock1.Bind 111 - Binds to the local port 111.

Port of remote host - WinSock1.RemotePort = 222 - Port to send datagrams to on the remote host.

Private Sub WinSock1_DataArrival (ByVal bytesTotal As Long)
  Dim strData As String
  WinSock1.GetData strData
  txtReceive.Text = strData
End Sub
Packet - The datagram information is contained in a packet that is sent or received through the WinSock1 object.

Receiving - An arriving packet generates an event which should be handled by the WinSock_DataArrival_(ByVal bytesTotal As Long) subroutine. Packet to receive must have storage of received data. A complete subroutine is:

Private Sub WinSock1_DataArrival _(ByVal bytesTotal As Long)
  Dim strData As String
  WinSock1.GetData strData
  txtReceive.Text = strData      ' Put received data in a text box
End Sub


Sending - Data sent from a machine (through port 111) to the destination machine at port number 222 through the network by:

WinSock1.SendData "Hello"

simpleChat VB

Note that the following contains portions from the Microsoft VB 6.0 documentation.

·  Protocol description

·  Symetric, one version runs on each of two hosts.

·  Each host can send one character to the other at any time due to the event driven natural of VB.

·  Message is sent as raw text string. A message is not considered to be terminated by anything.

·  Protocol not terminated by anything.

UDP Basics
In contrast to TCP, the UDP protocol doesn't require an explicit connection. To send data between two controls, three steps must be completed (on both sides of the connection):

  1. Set the RemoteHost property to the name of the other computer.
  2. Set the RemotePort property to the LocalPort property of the second control.
  3. Invoke the Bind method specifying the LocalPort to be used. (This method is discussed in greater detail below.)  Because both computers can be considered "equal" in the relationship, it could be called a peer-to-peer application. To demonstrate this, the code below creates a "chat" application that allows two people to "talk" in real time to each other:

To create a UDP Peer

  1. Create a new Standard EXE project.
  2. Draw a Winsock control on the form , it will be named WinSock1 automatically. To place on toolbar:
    • Project | Components | Check Microsoft Winsock Control
  1. Add two TextBox controls to the form. Name the one txtSend, and the other txtReceive.
  2. Add the code below for Peer 1.

 

UDP Peer 1 and 2

' UDP Peer 1

Private Sub Form_Load( )
  WinSock1.RemoteHost = "localhost"
  Winsock1.Protocol = sckUDPProtocol
  WinSock1.RemotePort = 222    ' Port to connect to.
  WinSock1.Bind 111                  ' Bind to the local port.
End Sub

Private Sub txtSend_Change( )
' Send text as soon as it's typed.
    WinSock1.SendData txtSend.Text
End Sub

Private Sub WinSock1_DataArrival (ByVal bytesTotal As Long)
   Dim strData As String
   WinSock1.GetData strData
   txtReceive.Text = strData
End Sub

'UDP Peer 2

Private Sub Form_Load( )
  WinSock1.RemoteHost = "localhost"
  Winsock1.Protocol = sckUDPProtocol
  WinSock1.RemotePort = 111    ' Port to connect to.
  WinSock1.Bind 222                  ' Bind to the local port.
End Sub

Private Sub txtSend_Change( )
' Send text as soon as it's typed.
    WinSock1.SendData txtSend.Text
End Sub

Private Sub WinSock1_DataArrival (ByVal bytesTotal As Long)
   Dim strData As String
   WinSock1.GetData strData
   txtReceive.Text = strData
End Sub

To create a second UDP Peer

  1. Run another copy of VB.
  2. Add a standard form to the project.
  3. Follow the same steps as above except reverse port assignments to allow running on one host

To try the example, press F5 to run the project, and type into the txtSend TextBox on either form. The text you type will appear in the txtReceive TextBox on the other form.

About the Bind Method
As shown in the code above, you must invoke the Bind method when creating a UDP application. The Bind method "reserves" a local port for use by the control to receive datagram packets. For example, when you bind the control to port number 1001, no other application can use that port to "listen" for packets.

The Bind method also features an optional second argument. If there is more than one network adapter present on the machine, the LocalIP argument allows you to specify which adapter to use. If you omit the argument, the control uses the first network adapter listed in the Network control panel dialog box of the computer's Control Panel Settings.

When using the UDP protocol, you can freely switch the RemoteHost and RemotePort properties while remaining bound to the same LocalPort. However, with the TCP protocol, you must close the connection before changing the RemoteHost and RemotePort properties.


Race game

Peer configurations typically interact more freely than the interaction characterized by a client and server taking turns as exemplified by a Web browser and server. A simple example where either peer can initiate an action is given below as a racing game where a peer moves on a grid, each attempting to crash into the other first. In this particular example the peers are identical so only one program exists. While the command/response interaction model typical of a client/server is relatively simple to program it is inappropriate where high interactivity is required, as is this case where either peer should be able to make multiple moves without waiting on the other to move.

A description of the racing game following unrestricted model.

 

// Racer.java - Use: java -cp . Racer <IP or DNS>

 

 
 import java.net.*;
 import java.io.*;
 
 public class Racer {
         
    public static void main(String args[]) throws Exception {
       int ioPort                = 111, toPort = ioPort;
       String toName        = "localhost";
       DatagramSocket ioSocket;
       Race race;
                                                                       // If both on same machine
       if (args.length > 0)                                    // use two port numbers
           toName = args[0] == null ? "localhost" : args[0];
           if (toName.equals("localhost")) toPort = 222; 
           try { ioSocket = new DatagramSocket(ioPort); }
           catch (Exception e) {                            // 222 already receiverPort
              ioPort = 222; toPort = 111;              // use 111 instead
              ioSocket = new DatagramSocket(ioPort);
           }
 
           System.out.println("Racer listening on port " + ioPort);
 
           if (ioPort == 111) 
               race = new Race('C', 0, 0, 'S', 2, 2);  // Start of race
           else
               race = new Race('S', 2, 2, 'C', 0, 0);  // Start of race
 
           ReceiverThread receiver = new ReceiverThread(ioSocket, race);
           SenderThread sender = new SenderThread(ioSocket, toName, toPort, race);
    }
 }
 

 


 

import java.net.*;
 import java.io.*;
 
 class SenderThread implements Runnable {
    DatagramSocket out;
    String toName;
 
    int toPort;
    Race race;
 
    public SenderThread(DatagramSocket out, String toName, int toPort, Race race) {
       this.out = out;
       this.toName = toName;
       this.toPort = toPort;
       this.race = race;
       new Thread(this).start();
    }
 
    public void run() {
       while(!race.over()) send();
    }
 
    public void send() {
       char c;
       byte sendBuffer[] = new byte[1];
       DatagramPacket sendPacket;
 
       try {   
           if (System.in.available() > 0 ) {
 
               c=(char) System.in.read();
               switch(c) {
                  case 'I': case 'J': case 'K': case 'M': 
                            sendBuffer[0] = (byte) c;
                            sendPacket = new DatagramPacket( sendBuffer, 1,
                                             InetAddress.getByName(toName), toPort);
                            out.send(sendPacket);
                            race.moveme(c);
               }
            }
       }
       catch(Exception e) { }
   }
 }
 

 


 

import java.net.*;
 import java.io.*;
 
 
 class ReceiverThread implements Runnable {
 
         DatagramSocket in;
         Race race;
 
         public ReceiverThread(DatagramSocket in, Race race) { 
                 this.in = in; 
                 this.race = race;
                 try { in.setSoTimeout(1000); } catch(Exception e) {};
                 new Thread(this).start();
         }
 
         public void run() {
                 while(!race.over()) receive();
         }
 
         public void receive() {
                 char c;
                 byte receiveBuffer[] = new byte[1];
                 DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, 1);
                 try {   
                         in.receive(receivePacket);
                         c = (char) receiveBuffer[0]; 
                         race.movethem(c);
                 }
                 catch(Exception e) {}   
         }
 }
 

 


 

class Race {
    int xme, yme, xthem, ythem;
    char me, them;
    char winner;
 
    public Race(char me, int xme, int yme, char them, int xthem, int ythem) { 
       this.me = me;             this.xme = xme;           this.yme = yme;
       this.them = them;       this.xthem = xthem;     this.ythem = ythem;
       winner='?';
       System.out.print("Keys\n I\nJ K\n M\n");
       print();
    }
 
    public void print() {
       for(int y=0; y < 20; y++) {
          for(int x=0; x<20; x++) 
             if (x==xme && y==yme) System.out.print(me); 
             else if (x==xthem && y==ythem) System.out.print(them);
                  else System.out.print('.');
          System.out.println();
       }
       if(over()) System.out.println(winner + " wins");
       System.out.println();
    }
         
    public synchronized void moveme(char direction) {
       switch (direction) {
          case 'I'  : if (yme > 0)  yme--; break;
          case 'J'  : if (xme > 0)  xme--; break;
          case 'K' : if (xme < 19) xme++; break;
          case 'M' : if (yme < 19) yme++; break;
       }
       if(xme == xthem && yme == ythem) { winner = me; them = '*'; me = '*'; }
       print();
     }
 
     public synchronized void movethem(char direction) {
        switch (direction) {
           case 'I' : if (ythem > 0)  ythem--; break;
           case 'J' : if (xthem > 0)  xthem--; break;
           case 'K' : if (xthem < 19) xthem++; break;
           case 'M' : if (ythem < 19) ythem++; break;
        }
        if(xme == xthem && yme == ythem) { winner = them; them = '*'; me = '*';}
        print();
     }
 
     public boolean over() {
        return winner != '?';
     }
 }
 

Use:

  1. Copy the Racer files from above.
    • Racer.java
    • Race.java
    • SenderThread.java
    • ReceiverThread.java
  1. Open two Command Prompt windows.
  2. Compile in one window with:
    • path = c:\progra~1\Java\jdk1.6.0_07\bin
    • javac *.java
  1. Execute in different windows by:
    • java -cp . Racer <IP or DNS>

 


Document last modified: