25.10. (Optional) Datagram Socket |
Clients and servers that communicate via a stream socket have a dedicated point-to-point channel between them. To communicate, they establish a connection, transmit the data, and then close the connection. The stream sockets use TCP (Transmission Control Protocol) for data transmission. Since TCP can detect lost transmissions and resubmit them, transmissions are lossless and reliable. All data sent via a stream socket is received in the same order in which it was sent.
In contrast, clients and servers that communicate via a datagram socket do not have a dedicated point-to-point channel. Data is transmitted using packets. Datagram sockets use UDP ( User Datagram Protocol), which cannot guarantee that the packets are not lost, or not received in duplicate, or received in the order in which they were sent. A datagram is an independent, self-contained message sent over the network whose arrival, arrival time, and content are not guaranteed .
In an analogy, a stream socket communication between a client and a server is like a telephone connection with a dedicated link. A datagram communication is like sending a letter through the post office. Your letter is contained in an envelope ( packet ). If the letter is too large, it may be sent in several envelopes (packets). There is no guarantee that your letter will arrive or that the envelopes will arrive in the order they were sent. One difference is that the letter will not arrive in duplicate, whereas a datagram packet may arrive in duplicate.
Most applications require reliable transmission between clients and servers. In such cases, it is best to use stream socket network communication. Some applications that you write to communicate over the network will not require the reliable, point-to-point channel provided by TCP. In such cases, datagram communication is more efficient.
The java.net package contains two classes to help you write Java programs that use datagrams to send and receive packets over the network: DatagramPacket and DatagramSocket . An application can send and receive DatagramPackets through a DatagramSocket .
The DatagramPacket class represents a datagram packet. Datagram packets are used to implement a connectionless packet-delivery service. Each message is routed from one machine to another based solely on information contained within the packet.
To create a DatagramPacket for delivery from a client, use the DatagramPacket(byte[] buf, int length, InetAddress host, int port) constructor. To create all other DatagramPackets , use the DatagramPacket(byte[] buf, int length) constructor, as shown in Figure 25.20. Once a datagram packet is created, you can use the getData and setData methods to obtain and set data in the packet.
The DatagramSocket class represents a socket for sending and receiving datagram packets. A datagram socket is the sending or receiving point for a packet-delivery service. Each packet sent or received on a datagram socket is individually addressed and routed. Multiple packets sent from one machine to another may be routed differently, and may arrive in any order.
To create a server DatagramSocket , use the constructor DatagramSocket(int port) , which binds the socket with the specified port on the local host machine.
To create a client DatagramSocket , use the constructor DatagramSocket() , which binds the socket with any available port on the local host machine.
To send data, you need to create a packet, fill in the contents, specify the Internet address and port number for the receiver, and invoke the send(packet) method on a DatagramSocket .
To receive data, you have to create an empty packet and invoke the receive(packet) method on a DatagramSocket .
Datagram programming is different from stream socket programming in the sense that there is no concept of a ServerSocket for datagrams. Both client and server use DatagramSocket to send and receive packets, as shown in Figure 25.21.
Normally, you designate one application as the server and create a DatagramSocket with the specified port using the constructor DatagramSocket(port) . A client can create a DatagramSocket without specifying a port number. The port number will be dynamically chosen at runtime. When a client sends a packet to the server, the client's IP address and port number are contained in the packet. The server can retrieve it from the packet and use it to send the packet back to the client.
To demonstrate , let us rewrite the client and server programs in Listings 25.1 and 25.2 using datagrams rather than socket streams. The client sends the radius to a server. The server receives this information, uses it to find the area, and then sends the area to the client.
Listing 25.15 gives the server, and Listing 25.16 gives the client. A sample run of the program is shown in Figure 25.22.
1 import java.io.*; 2 import java.net.*; 3 import java.util.*; 4 import java.awt.*; 5 import javax.swing.*; 6 7 public class DatagramServer extends JFrame { 8 // Text area for displaying contents 9 private JTextArea jta = new JTextArea(); 10 11 // The byte array for sending and receiving datagram packets 12 private byte [] buf = new byte [ 256 ]; 13 14 public static void main(String[] args) { 15 new DatagramServer(); 16 } 17 18 public Server() { 19 // Place text area on the frame 20 setLayout( new BorderLayout()); 21 add( new JScrollPane(jta), BorderLayout.CENTER); 22 23 setTitle( "DatagramServer" ); 24 setSize( 500 , 300 ); 25 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 26 setVisible( true ); // It is necessary to show the frame here! 27 28 try { 29 // Create a server socket 30 DatagramSocket socket = new DatagramSocket( 8000 ); 31 jta.append( "Server started at " + new Date() + '\n' ); 32 33 // Create a packet for receiving data 34 DatagramPacket receivePacket = 35 new DatagramPacket(buf, buf.length); 36 37 // Create a packet for sending data 38 DatagramPacket sendPacket = 39 new DatagramPacket(buf, buf.length); 40 41 while ( true ) { 42 // Initialize buffer for each iteration 43 Arrays.fill(buf, ( byte ) ); 44 45 // Receive radius from the client in a packet 46 socket.receive(receivePacket); 47 jta.append( "The client host name is " + 48 receivePacket.getAddress().getHostName() + 49 " and port number is " + receivePacket.getPort() + '\n' ); 50 jta.append( "Radius received from client is " + 51 new String(buf).trim() + '\n' ); 52 53 // Compute area 54 double radius = Double.parseDouble( new String(buf).trim()); 55 double area = radius * radius * Math.PI; 56 jta.append( "Area is " + area + '\n' ); 57 58 // Send area to the client in a packet 59 sendPacket.setAddress(receivePacket.getAddress()); 60 sendPacket.setPort(receivePacket.getPort()); 61 sendPacket.setData( new Double(area).toString().getBytes()); 62 socket.send(sendPacket); 63 } 64 } 65 catch (IOException ex) { 66 ex.printStackTrace(); 67 } 68 } 69 } |
1 import java.io.*; 2 import java.net.*; 3 import java.util.*; 4 import java.awt.*; 5 import java.awt.event.*; 6 import javax.swing.*; 7 8 public class DatagramClient extends JFrame { 9 // Text field for receiving radius 10 private JTextField jtf = new JTextField(); 11 12 // Text area to display contents 13 private JTextArea jta = new JTextArea(); 14 15 // Datagram socket 16 private DatagramSocket socket; 17 18 // The byte array for sending and receiving datagram packets 19 private byte [] buf = new byte [ 256 ]; 20 21 // Server InetAddress 22 private InetAddress address; 23 24 // The packet sent to the server 25 private DatagramPacket sendPacket; 26 27 // The packet received from the server 28 private DatagramPacket receivePacket; 29 30 public static void main(String[] args) { 31 new DatagramClient(); 32 } 33 34 public DatagramClient() { 35 // Panel p to hold the label and text field 36 JPanel p = new JPanel(); 37 p.setLayout( new BorderLayout()); 38 p.add( new JLabel( "Enter radius" ), BorderLayout.WEST); 39 p.add(jtf, BorderLayout.CENTER); 40 jtf.setHorizontalAlignment(JTextField.RIGHT); 41 42 setLayout( new BorderLayout()); 43 add(p, BorderLayout.NORTH); 44 add( new JScrollPane(jta), BorderLayout.CENTER); 45 46 jtf.addActionListener( new ButtonListener()); // Register listener 47 48 setTitle( "DatagramClient" ); 49 setSize( 500 , 300 ); 50 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 51 setVisible( true ); // It is necessary to show the frame here! 52 53 try { 54 // get a datagram socket 55 socket = new DatagramSocket(); 56 address = InetAddress.getByName( "localhost" ); 57 sendPacket = 58 new DatagramPacket(buf, buf.length, address, 8000 ); 59 receivePacket = new DatagramPacket(buf, buf.length); 60 } 61 catch (IOException ex) { 62 ex.printStackTrace(); 63 } 64 } 65 66 private class ButtonListener implements ActionListener { 67 public void actionPerformed(ActionEvent e) { 68 try { 69 // Initialize buffer for each iteration 70 Arrays.fill(buf, ( byte ) ); 71 72 // Send radius to the server in a packet 73 sendPacket.setData(jtf.getText().trim().getBytes()); 74 socket.send(sendPacket); 75 76 // Receive area from the server in a packet 77 socket.receive(receivePacket); 78 79 // Display to the text area 80 jta.append( "Radius is " + jtf.getText().trim() + "\n" ); 81 jta.append( "Area received from the server is " 82 + Double.parseDouble( new String(buf).trim()) + '\n' ); 83 } 84 catch (IOException ex) { 85 ex.printStackTrace(); 86 } 87 } 88 } 89 } |
Since datagrams are connectionless, a DatagramPacket can be sent to multiple clients, and multiple clients can receive a packet from the same server. As shown in this example, you can launch multiple clients. Each client sends the radius to the server, and the server sends the area back to the client.
The server creates a DatagramSocket on port 8000 (line 30 in DatagramServer.java). No DatagramSocket can be created again on the same port. The client creates a DatagramSocket on an available port (line 55 in DatagramClient.java). The port number is dynamically assigned to the socket. You can launch multiple clients simultaneously , and each client's datagram socket will be different.
The client creates a DatagramPacket named sendPacket for delivery to the server (lines 57 “58 in DatagramClient.java). The DatagramPacket contains the server address and port number. The client creates another DatagramPacket named receivePacket (line 59), which is used for receiving packets from the server. This packet does not need to contain any address or port number.
A user enters a radius in the text field in the client. When the user presses the Enter key on the text field, the radius value in the text field is put into the packet and sent to the server (lines 73 “74 in DatagramClient.java). The server receives the packet (line 46 in DatagramServer.java), extracts the data from the byte array buf , and computes the area (lines 54 “55 in DatagramServer.java). The server then builds a packet that contains the area value in the buffer, the client's address, and the port number, and sends the packet to the client (lines 59 “62). The client receives the packet (line 77 in DatagramClient.java) and displays the result in the text area.
The data in the packet is stored in a byte array. To send a numerical value, you need to convert it into a string and then store it in the array as bytes, using the getBytes() method in the String class (line 62 in DatagramServer.java and line 74 in DatagramClient.java). To convert the array into a number, first convert it into a string, and then convert it into a number using the static parseDouble method in the Double class (line 54 in DatagramServer.java and line 82 in DatagramClient.java).
Note
The port numbers for the stream socket and the datagram socket are not related . You can use the same port number for a stream socket and a datagram socket simultaneously. |