Document last modified:
Overview Networking generally come in two flavors,
datagram and connection-oriented, that is UDP (User Datagram Protocol) and TCP
(Transport Control Protocol) respectively. Connection-oriented service generally
operates with a client and server following the steps of 1) client requests
connection to server, 2) server accepts connection, 3) bi-directional
communication can occur through the connection, 4) either may close the
connection. Most languages such as Java and VB support abstractions for the
implementation of these necessary operations.
Example - The following are three examples of a client and server that
uses TCP/IP related services. The client connects (a TCP service) to an echo
server to send (a TCP service) and receive (a TCP service) an echo response from
the server as in the figure at right. TCP service calls in each language are
given below in bold. Note that the reality
of TCP service implementation is never as
neat as the abstraction, for example, it is not obvious whether the socket
creation call (underlined in the C++ example below) is a TCP or operating
system service. One view that helps clarify the distinction is that the TCP
services on one host should communicate with another host. The socket
function is instead an operating system function that allocates resources on the
local host only and does not communicate with another host. The C++ send
function is obviously a TCP service since it literally sends data using the TCP
connection from the client to the echo server.
Ports and Sockets - TCP identifies an application on a specific host
by the host IP number and port number used by the application. For
example, the main IUS
Web server application is at www.ius.edu and port 80. The
port number is the external reference to the application, that is the Web server
application is referred to as port 80. Internal to the application the port
number is mapped to a socket, the socket is used by the application to
communicate over the TCP connection. This is illustrated in a typical
configuration at right where the client has requested a connection on the
server host through port 888. Once the connection is made the two host
applications can communicate through the respective socket on each.
Protocol - For a client and server or peers to communicate, each must implement the appropriate protocols. The echo protocol is relatively simple and is stated below. In the client/server interaction, the client connects to the server and sends a message string such as Hi which the server receives and echos back the Hi to the client. The client indicates that communication is finished by closing the connection which the server receives and closes its end. To coordinate the connect and disconnect between the client and server a simple protocol is used between the two:
It is important to note that any client or server following the protocol can be used interchangeably. The following provides examples of the protocol implementation in Java, C++, and Visual Basic.
telnet mail-server.ucs.indiana.edu 25
and send yourself an email.
A request from the client to the server return the contents of the HTTP file homepage.htm on the server at the default directory would be the following two lines:
GET /homepage.htm HTTP/1.0
Client - The client should:
The server should return the text of the IUS homepage to the telnet
session. This is essentially what a browser must do to get the
default Web server page. You should see the same text displayed by your
client.
Server - Following the HTTPD protocol the HTTP server should:
- Listen on port 80 for a connection
- When a connection is accepted, parse the input line for GET /path HTTP/1.0 to determine the path location of the file and its name. The Java and Python sections have suggestions on parsing the file name.
- Print to the client the three lines (the last is blank):
- HTTP/1.0 200 OK
- Content-Type: text/html
- Copy the HTML formatted file in the path to the client connection. If no path is specified, copy a default file named homepage.htm. The last line sent must end with a carriage return and line feed ('\r\n').
- When the file has been sent, the connection is closed by the server. It would then normally wait for another connection.
Test your server in two ways:
- Turn off any other HTTP servers if one is running on the computer. Test your server by connecting using telnet localhost 80, then typing the two lines (path is the file path on the server machine):
- GET /path HTTP/1.0
- Using a browser, open a connection using http://localhost/path, the HTML file should be displayed by the browser.
Hints: Try a minimal server first, one that just sends out the appropriate HTTP and HTML to a client no matter what the client requests.
The server should:
- HTTP/1.0 200 OK
- Content-Type: text/html
- <H1>Hello world</H1>
Java
In the following examples, the communication between the client and server is
implemented by corresponding function calls in each row of the table.
| Client | Server |
| ServerSocket connection = new ServerSocket( 888 ); | |
| Socket s = new Socket("localhost", 888 ); | Socket s = connection.accept(); |
| out.print(to); | from=in.readLine( ) |
| from=in.readLine( ) | out.write(from); |
// echoclient.java - Use: java -cp . echoclient <IP or DNS>
import java.net.*;
import java.io.*;
class echoclient {
public static void main(String args[]) throws Exception {
String to, from;
// Echo server IP and port
Socket s = new Socket(
(args.length == 0) ? "localhost": args[0], 888 );
// Buffered socket input &output
BufferedReader in = new BufferedReader(
new InputStreamReader(
s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
BufferedReader keybd = new BufferedReader(
new InputStreamReader(System.in) );
System.out.println("Blank line to end");
// Read keyboard and send
while( (to=keybd.readLine()) != null && !to.equals("")) {
out.print(to + "\r\n"); // to server until a blank line
from=in.readLine(); // Wait for echo
System.out.print( from );
}
s.close(); // Close connection
}
}
|
// echoserver.java Use: java -cp . echoserver
import java.net.*;
import java.io.*;
class echoserver {
public static void main(String args[]) throws Exception {
String from;
ServerSocket connection = new ServerSocket( 888 );
// Wait for connection
Socket s = connection.accept();
// Socket input & output
BufferedReader in = new BufferedReader(
new InputStreamReader(
s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
System.out.println("Connected");
// Echo client text till ""
while( (from=in.readLine()) != null && !from.equals("")) {
System.out.println( from );
out.print(from + "\r\n"); // echo to client
}
s.close(); // Close connection
System.out.println("Disconnected");
}
}
|
- FileInputStream to open a file.
- available( ) to check on the number of bytes available in a file.
- read( ) to read an int from a file.
The following opens the file readfile.java and copies the contents to the console.
// readfile.java - Use: java -cp . readfile import java.io.*; class readfile { public static void main(String args[]) throws Exception { int ch; FileInputStream in = new FileInputStream("c:/temp/readfile.java"); while( in.available() > 0 ) { ch = in.read(); System.out.print((char) ch); } in.close(); } }
Parsing Client String - The path in the string GET /path HTTP/1.0 can be parsed by:
if (str != null && str.startsWith("GET /")) { // Locate a string with GET / at start
String p[] = str.split(" "); // Split into 3 strings, stored in array p,
// p[0] = "GET", p[1] = "/path", p[2] = "HTTP/1.0".
}
Minimal HTTP server
When a connection is accepted on port 80, send HTTP reply and Hello World, then disconnect
import java.net.*; import java.io.*; class server { public static void main(String args[]) throws Exception { ServerSocket connection = new ServerSocket( 80 ); // Wait for connection Socket s = connection.accept(); // Socket output PrintStream out = new PrintStream(s.getOutputStream()); // Send HTTP reply and Hello World out.print("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<H1>Hello world</H1>\r\n\r\n"); s.close(); // Close connection } }
Python
In the following examples, the communication between the client and server is
implemented by corresponding function calls in each row of the table.
| Client | Server |
| s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
| s.bind((HOST, PORT)) | |
| s.listen(1) | |
| s.connect((HOST, PORT)) | conn, addr = s.accept() |
| s.send('Hello world') | data = conn.recv(1024) |
| data = s.recv(1024) | conn.send(data) |
# echoclient.py
import socket, sys
HOST = sys.argv[1] # The remote host
PORT = 8888 # Port used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
while True :
message = raw_input('Send:')
if not message : break
s.send(message)
data = s.recv(1024)
print 'Received', `data`
s.close()
|
# echoserver.py
import socket
HOST = '' # Symbolic name
PORT = 8888 # Arbitrary non-privileged port
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
print 'Connected by', addr
while True:
data = conn.recv(1024)
if not data: break # Client disconnected
conn.send(data)
conn.close()
s.close()
|
Receiving full lines
HTTP uses a blank line to terminate a message and all lines in the message are terminated by '\r\n' (carriage return and line feed characters). Because some clients (telnet) send a single character at a time while others (IE browser) send the complete message at once, you'll need to receive complete lines in order to accommodate both. For example, telnet sends GET as G, E, T but you'll find it simpler to collect all characters until receiving a full line terminated by '\r\n'.
The following recv's from a socket s until a line, anything terminated by '\r\n', is received. Note that this suffers from potential buffer over-run, a favorite attack point of hackers.
// recvLine.py import socket HOST = '' # Symbolic name PORT = 8888 # Arbitrary non-privileged port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind((HOST, PORT)) s.listen(1) conn, addr = s.accept()From = '' while True : data=conn.recv(1024) # Read from client From=From+data if From.endswith( '\r\n' ) : breakprint From s.close() conn.close()
File Input - Python file input uses:
- file to open a file.
- readline( ) to read a line from a file.
The following opens the file c:/temp/readfile.py and copies the contents to the console.
// readfile.py f = file('c:/temp/readfile.py') while True : data = f.readline() if not data : break print data
Parsing Client String - The path in the string GET /path HTTP/1.0 can be parsed by:
p = 'GET /path HTTP/1.0'.split() # Split into list of strings, stored in p,
# p[0] = "GET", p[1] = "/path", p[2] = "HTTP/1.0".
Minimal HTTP server
When a connection is accepted on port 80, send HTTP reply and Hello World, then disconnect
import socket s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', 80)) s.listen(1) conn, addr = s.accept() print conn.recv(1024) # From client # To client conn.send('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<H1>Hello world</H1>\r\n\r\n') conn.close() s.close()
In the following examples, the communication between the client and server is
implemented by corresponding function calls in each row of the table.
| Client | Server |
| connect(s,(struct sockaddr*)&sin,sizeof(sin)) | accept(s,(struct sockaddr*)&sin,&sinlen ) |
| send(s,buffer,strlen(buffer)+1,0) | recv(h,buffer,sizeof(buffer),0) |
| recv(s,buffer,sizeof(buffer),0) | send(h,buffer,strlen(buffer)+1,0) |
// echoClient.cpp - Use: echoClient <IP> or <DNS>
// Visual C++ Project | Settings | Link | Object/Library modules | ws2_32.lib
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <iostream.h>
int main(int argc, char* argv[])
{ char buffer[128];
int retval;
unsigned int addr=0;
struct sockaddr_in sin;
struct hostent *host;
WSADATA wsaData;
SOCKET s;
WSAStartup(0x202,&wsaData); // Startup
host = gethostbyname(argv[1]); // Try DNS lookup
if (!host) // DNS failed try as IP
addr = inet_addr(argv[1]);
if ((!host) && (addr == INADDR_NONE) ) { // DNS and IP failed
cout << "Unable to resolve " << argv[1] << '\n';
return -1;
}
if (host != NULL) { // Copy server info
memcpy(&(sin.sin_addr),host->h_addr,host->h_length);
sin.sin_family = host->h_addrtype;
}
else {
sin.sin_addr.s_addr = addr;
sin.sin_family = AF_INET;
}
sin.sin_port = htons(889);
// Create socket
if ((s = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET){
cout << "socket() failed with error " << WSAGetLastError() << '\n';
return -1;
} // Connect to server port
if (connect(s,(struct sockaddr*)&sin,sizeof(sin)) == INVALID_SOCKET) {
cout << "connect() failed with error " << WSAGetLastError() << '\n';
return -1;
}
send(s,"Hi",2,0); // Send to echo server
send(s,"\0",1,0); // Send to echo server
retval=recv(s,buffer,sizeof(buffer),0);
// Receive echo from server
while((retval=recv(s,buffer,sizeof(buffer),0) > 0) && buffer[0] != '\0') {
// 0 terminate buffer
if(retval < sizeof(buffer)) buffer[retval]='\0';
// print the echo
cout << "echoed " << buffer << "\n"; flush(cout);
}
closesocket(s); // Close connection
cout << "Closed connection.\n"; flush(cout);
return 0;
}
|
// echoServer.cpp - Use: echoServer
// Visual C++ Project | Settings | Link | Object/Library modules | ws2_32.lib
#define WIN32_LEAN_AND_MEAN
#include <winsock2.h>
#include <iostream.h>
int main(void)
{ char buffer[128];
int retval, sinlen;
struct sockaddr_in sin;
WSADATA wsaData;
SOCKET s, h;
WSAStartup(0x202,&wsaData);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(888); // Port 888
// SOCK_STREAM is TCP
if ((s = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET){
cout << "socket() failed with error " << WSAGetLastError() << '\n';
return -1;
} // Bind socket to local port
if (bind(s,(struct sockaddr*)&sin,sizeof(sin) ) == SOCKET_ERROR) {
cout << "bind() failed with error " << WSAGetLastError() << '\n';
return -1;
} // Listen for socket connection
if (listen(s,1)== SOCK_ERROR) {
cout << "listen() failed with error " << WSAGetLastError() << '\n';
return -1;
}
sinlen = sizeof(sin);
while(1) { // Accept incoming connection
if ((h=accept(s,(struct sockaddr*)&sin,&sinlen )) == INVALID_SOCKET) {
cout << "accept() failed with error " << WSAGetLastError() << '\n';
return -1;
}
cout << "Opened connection.\n"; flush(cout);
// receive while connection open
while((retval=recv(h,buffer,sizeof(buffer),0)) != SOCKET_ERROR && retval > 0){
if(retval < sizeof(buffer)) // Make into 0 terminated string
buffer[retval]='\0';
cout << "Received " << buffer << "\n"; flush(cout);
// Echo what was received
send(h,buffer,strlen(buffer)+1,0);
}
closesocket(h);
cout << "Closed connection.\n"; flush(cout);
}
return 0;
}
|
In the following examples, the communication between the client and server is implemented by corresponding function calls in each row of the table.
| Client | Server |
| echoClient.RemoteHost = "127.0.0.1" echoClient.RemotePort = 888 echoClient.Connect |
echoServer.LocalPort = 888 echoServer.Listen echoServer.Accept requestID |
| echoClient.GetData strData | echoServer.GetData strData |
| echoClient.SendData "echo " + txtOutput.Text | echoServer.SendData strData |
| ' Client Private Sub Form_Load() ' The name of the Winsock control is echoClient. ' Note: to specify a remote host, you can use ' either the IP address (ex: "121.111.1.1") or name. echoClient.RemoteHost = "127.0.0.1" echoClient.RemotePort = 888 End Sub Private Sub cmdDisconnect_Click() ' Disconnect Private Sub cmdSend_Click() ' Send data to server
Private Sub cmdConnect_Click() ' Connect to server Private Sub echoClient_DataArrival(ByVal bytesTotal As Long) |
' Server Private Sub Form_Load() ' Listen for connection on port 888' echoServer.LocalPort = 888 echoServer.Listen End Sub Private Sub echoServer_Close() Private Sub echoServer_ConnectionRequest(ByVal requestID As Long) Private Sub echoServer_DataArrival(ByVal bytesTotal As Long) echoServer.GetData strData |
Controls
|
Controls
|
Networking in VB .Net can be much closer to C++. The following uses synchronized sockets, those that do not raise an event. Though simpler to understand, not practical without multithreading. The VB way is to use asynchronous sockets that raise an event whenever communication occurs on the connection.
| Client | Server |
| Dim remoteEP As New _ IPEndPoint(Dns.Resolve("localhost").AddressList(0), 888) Dim client As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) client.Connect(remoteEP) |
Dim localEndPoint As _ New IPEndPoint(Dns.Resolve("localhost").AddressList(0), 888) Dim s As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) s.Bind(localEndPoint) s.Listen(10) Dim server As Socket = s.Accept() |
| client.Receive(bytes) | server.Receive(bytes) |
| client.Send(msg) | server.Send(bytes) |
| ' Client Imports System Imports System.Net Imports System.Net.Sockets Imports System.Text Public Class echoClient Public Shared Sub Start() Dim bytes(1024) As Byte ' Data buffer for incoming data. Try ' Connect to a remote device. ' Establish the remote endpoint for the socket. Dim remoteEP As New _ IPEndPoint(Dns.Resolve("localhost").AddressList(0), 888) ' Create a TCP/IP socket. Dim client As New Socket(AddressFamily.InterNetwork, _ SocketType.Stream, ProtocolType.Tcp) Try ' Connect the socket to the remote endpoint. client.Connect(remoteEP) Console.WriteLine("Socket connected to {0}", _ client.RemoteEndPoint.ToString()) ' Encode the data string into a byte array. Dim msg As Byte() = _ Encoding.ASCII.GetBytes("Testing<EOF>") ' Send the data through the socket. Dim bytesSent As Integer = client.Send(msg) ' Receive the response from the remote device. Dim bytesRec As Integer = client.Receive(bytes) Console.WriteLine("Echoed test = {0}", _ Encoding.ASCII.GetString(bytes, 0, bytesRec)) ' Release the socket. client.Shutdown(SocketShutdown.Both) client.Close() Catch e As Exception Console.WriteLine(e.ToString()) End Try Catch e As Exception Console.WriteLine(e.ToString()) End Try End Sub End Class Module Module1 Sub Main() Dim echoclient As New echoClient() echoclient.Start() End Sub End Module |
' Server
Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports Microsoft.VisualBasic
Public Class echoServer
Public Shared receivedData As String = Nothing
Public Shared Sub Start()
Dim bytes() As Byte = New [Byte](1024) {}
' Establish the local endpoint for the socket.
Dim localEndPoint As _
New IPEndPoint(Dns.Resolve("localhost").AddressList(0), 888)
' Create a TCP/IP socket.
Dim s As New Socket(AddressFamily.InterNetwork, _
SocketType.Stream, ProtocolType.Tcp)
Try ' Bind socket to the local endpoint, listen for incoming connections.
s.Bind(localEndPoint)
s.Listen(10)
While True ' Start listening for connections.
Console.WriteLine("Waiting for a connection...")
Dim server As Socket = s.Accept()
receivedData = ""
' An incoming connection needs to be processed.
Do Until receivedData.IndexOf("<EOF>") > -1
bytes = New Byte(1024) {}
Dim msg As Integer = server.Receive(bytes)
receivedData += Encoding.ASCII.GetString(bytes, 0, msg)
Console.WriteLine("Text received : {0}", receivedData)
server.Send(bytes)
Loop
' Echo the data back to the client.
server.Shutdown(SocketShutdown.Both)
server.Close()
End While
Catch e As Exception
Console.WriteLine(e.ToString())
End Try
End Sub
End Class
Module Module1
Sub Main()
Dim echoserver As New echoServer()
echoserver.Start()
End Sub
End Module
|
A key advantage of Java for network applications is the relative simplicity of communication using datagram (User Datagram Protocol) and connection services (Transport Control Protocol). The primary advantage of connection-oriented services is input and output can be treated as a stream of reliable data, much as reading or writing a file. The following discusses the key parts of Java for using networking services.
Port - A workstation is uniquely identified by its IP number such as 149.160.51.113 associated with an NIC but may be communicating with multiple sites. To maintain each communication separately datagrams are sent and received through unique ports. 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 (888) and receive through one bi-directional port. It is important to note that the sender port number must match the receiver port number.
Socket - A connection socket provides a reliable, bi-directional communication path between two applications. Two types of socket can be created, server and client with the key difference that server sockets wait for a connection while client sockets initiate connections.
Client - Socket s = new Socket("www.abc.com", 888); creates a client socket that attempts a connection to a server www.abc.com on port 888. If the connection is unavailable an error condition results.
Server - ServerSocket c = new ServerSocket( 888); creates a server socket that listens on port 888 but does not accept a connection from a client. Socket sc = c.accept(); waits for a connection via the server socket returning a regular connection socket.
Communication - Applications can perform input and output over a connection exactly as with any I/O stream from file, console, etc. such as read on InputStream, write on OutputStream objects, and print on PrintStream objects..
Input - InputStream in = sc.getInputStream(); int c = in.read(); reads an integer (which could be cast to a character, etc.) from the connection on socket sc.
Often simplest to use buffered input. BufferedReader class for input supports input of a complete string or individual characters and has the following useful characteristics:
Read a line of text. A line is considered to be
terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a
carriage return followed immediately by a linefeed.
Returns:
A String containing the
contents of the line, not including any line-termination characters, or null
if the end of
the stream has been
reached
String from;
Socket s = connection.accept(); // Wait for connection
BufferedReader in = new BufferedReader( // Socket input
new InputStreamReader( s.getInputStream() ) );
while( (from=in.readLine()) != null && !from.equals("")) {
System.out.println( from );
}
|
public int read(char cbuf[], int off, int len) throws IOException
Read characters into a portion of an array. Ordinarily this method takes characters from this stream's character buffer, filling it from the underlying stream as necessary. If, however, the buffer is empty, the mark is not valid, and the requested length is at least as large as the buffer, then this method will read characters directly from the underlying stream into the given array. Thus redundant BufferedReaders will not copy data unnecessarily.
Parameters:
cbuf - Destination buffer
off - Offset at which to
start storing characters
len - Maximum number of
characters to read
Returns:
The number of bytes read,
or -1 if the end of the stream has been reached
Output - Easiest to use is:
PrintStream out = new PrintStream( sc.getOutputStream );
out.print("Hello");
would write an "Hello" to the connection on socket sc. PrintStream objects can output strings, numbers, etc.
InetAddress - The IP address representation of a system. InetAddress(getLocalHost())returns the address of the machine executing the statement.
File Input - Java file input uses:
- FileInputStream to open a file.
- available( ) to check on the number of bytes available in a file.
- read( ) to read an int from a file.
The following opens the file readfile.java and copies the contents to the console.
// readfile.java - Use: java -cp . readfile import java.io.*; class readfile { public static void main(String args[]) throws Exception { int ch; FileInputStream in = new FileInputStream("c:/temp/readfile.java"); while( in.available() > 0 ) { ch = in.read(); System.out.print((char) ch); } in.close(); } }
Parsing Client String - The path in the string GET /path HTTP/1.0 can be parsed by:
if (str != null && str.startsWith("GET /")) { // Locate a string with GET / at start
String p[] = str.split(" "); // Split into 3 strings, stored in array p,
// p[0] = "GET", p[1] = "/path", p[2] = "HTTP/1.0".
}
Visual C++ supports TCP/IP services through Windows Sockets and requires that the ws2_32.lib library be linked to the executeable. To add the library in Visual C++ enter:
Project | Settings | Link | Object/Library modules | ws2_32.lib
Port - Same concept as Java. In our example, the server used one port (888). The server must bind the port and socket, the client would not normally bind a specific port.
sin.sin_port = htons(888); // Port 888
// SOCK_STREAM is TCP
if ((s = socket(AF_INET, SOCK_STREAM,0)) == INVALID_SOCKET){
cout << "socket() failed with error " << WSAGetLastError() << '\n';
return -1;
}
// Bind socket to local port
if (bind(s,(struct sockaddr*)&sin,sizeof(sin) ) == SOCKET_ERROR) {
cout << "bind() failed with error " << WSAGetLastError() << '\n';
return -1;
}
Socket - The TCP socket is created as SOCK_STREAM. Sockets are the same for server and client, the key difference is use, the server listens for connections on sockets while client sockets initiate connections.
Client - Connects to a server port.
if (connect(s,(struct sockaddr*)&sin,sizeof(sin)) == INVALID_SOCKET) {
cout << "connect() failed with error " << WSAGetLastError() << '\n';
return -1;
}
Server - Listens for connections and waits to accept a connection from a client.
lister(s,1);
sinlen = sizeof(sin);
h=accept(s,(struct sockaddr*)&sin,&sinlen );
Input - recv(h, buffer, sizeof(buffer), 0); Blocks for data on socket h and fill buffer with up to the size of buffer characters.
Output - send(h, buffer, strlen(buffer)+1 ,0); Sends buffer through the socket h.
gethostbyname - gethostbyname("www.ius.edu"); Returns the hostent structure information.
inet_addr - inet_addr("149.160.16.200"); Converts an IPv4 number to address appropriate for in_addr structure.
File Input - Use:
- open to open a fstream for input or output.
- eof to test for end of file.
- get to input from an opened file.
The following prints the file readfile.java to the console.
#include <fstream.h>
#include <iostream.h>
void main(void) {
fstream in;
char ch;
in.open( "c:\\temp\\readfile.java", ios::in);
while ( !in.eof() ) {
in.get(ch);
cout << ch;
}
in.close();
}
|
Visual Basic implements connection services (Transport Control Protocol) using a tool named Winsock, Windows Sockets, which has the same capabilities as Java. The following discusses the key parts of VB for using networking services that differ from the Java discussion.
One key distinction is that VB uses graphical tools, the tool for Winsock
appears as
. Since it is not
normally on the toolbar, it must be placed there by:
The component can then be placed on a form that uses Winsock objects.
Client - echoClient.RemoteHost = "www.abc.com"
echoClient.RemotePort = 888
echoClient.Connect
creates a client socket that attempts a connection to a server www.abc.com on port 888. If the connection is unavailable an error condition results.
Server - echoServer.LocalPort = 888
echoServer.Listen
creates a server socket that listens on port 888 to accept a connection from a client.
The server must implement a procedure that is executed when a connection is requested. It is supplied the ID of the machine making the request as in the following:
Private Sub echoServer_ConnectionRequest(ByVal requestID As Long)
echoServer.Accept requestID
End Sub
Input - When data arrives on a connection an event occurs to execute the DataArrival procedure.
Sub echoServer_DataArrival(ByVal
bytesTotal As Long)
Dim strData
as string
echoServer.GetData strData
End Sub
reads a string from the connection.
Output - Send data out the connection by:
echoServer.SendData "Hello"
Closing - When the connection is finished both side should close the connection by:
echoServer.Close
File Input - Use:
- Open to open a file for input or output.
- EOF to test for end of file.
- Input to input from an opened file.
The following prints the file readfile.java to a picture object when clicked.
Sub Picture1_Click()
Dim line As String
Open "c:\temp\readfile.java" For Input As #1
While (Not EOF(1))
Input #1, line
Picture1.Print line
Wend
Close #1
End Sub
|
Each of the echo server implementations could manage only a single connection at a time but real servers must be capable of handling multiple simultaneous connections. The solution most often used is to perform the operation of a single server by a single thread of execution. Each client connection causes a new server to be started to service that connection alone and each connection close causes its server to be terminated.
The following is a Java implementation of a threaded echo server that can support an arbitrary number of simultaneous client connections. Java language includes very straightforward mechanisms that make implementing threaded servers relatively simple. In the following example note that lines 18-32 are copied from the single connection server implemented above. The remaining lines create and start a thread for each connection. One important programming element necessary for threading is the ability to create a set of resources for exclusive use by each thread. In the example, each serverThread is an object with independent data created by:
new serverThread(s);
The serverThread class inherits from class Runnable basic thread operations. The statement:
new Thread(this).start( );
calls the run( ) function to start the thread execution. The thread executes within the run( ) function until the client closes the connection. When the run( ) function is exited, the thread terminates.
Note that threads are supported by libraries in C++. Visual Basic supports
threads and event driven method execution, for a good discussion of Visual Basic
threading see
http://www.desaware.com/articles/threadingL3.htm.
|
Most client/server configurations interact in a command/response mode where the client sends a command and the server returns a response with the interaction characterized by the client and server taking turns. One example is a Web browser and server. A simple example is given below of a racing game where the client takes turns with server moving on a grid, each attempting to crash into the other first. While the command/response interaction model is relatively simple to program it is inappropriate where high interactivity is required, as is this case where either the client or server should be able to make multiple moves without waiting on the other to move. Instead each are forced to take turns making a move, first the client then the server, the interaction is obvious in the following code fragments.
Client
|
Server
|
A description of the racing game following the command/response model.
requesting the
connection. As noted above, the client sends the first move, then waits for
the server's move. The server waits for the client's move then sends its move.
Otherwise the race server and client are nearly identical. The race server
initializes the race by placing the two racers on the grid, it is required
that the client places the racers at the same coordinates. A sender and
receiver objects are created to handle sending and receiving racer
moves and notifying the race object of moves so that the race is updated. The
sender and receiver are repeatedly called until the race is over.Problem - When racing the client and server must alternate moves because each alternates calls to the send and receive methods.
Question
// Racerserver.java - Use: java -cp . Racerserver
import java.net.*;
import java.io.*;
public class Racerserver {
public static void main(String args[]) throws Exception {
// Server IP and port
ServerSocket connection = new ServerSocket( 888 );
Socket s = connection.accept(); // Wait for connection
BufferedReader in = new BufferedReader( // Buffered socket input and output
new InputStreamReader( s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
Race race = new Race('S', 2, 2, 'C', 0, 0); // Start race
Receiver receiver = new Receiver(in, race);
Sender sender = new Sender(out, race);
do {
receiver.receive(); // Receive client move
sender.send(); // Send server move
} while(!race.over());
s.close(); // Close connection
}
}
|
// Racerclient.java - Use: java -cp . Racerclient <IP or DNS>
import java.net.*;
import java.io.*;
public class Racerclient {
public static void main(String args[]) throws Exception {
// Server IP and port
Socket s = new Socket( (args.length == 0) ? "localhost" : args[0], 888 );
BufferedReader in = new BufferedReader( // Buffered socket input and output
new InputStreamReader( s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
Race race = new Race('C', 0, 0, 'S', 2, 2); // Start of race
Receiver receiver = new Receiver(in, race);
Sender sender = new Sender(out, race);
do {
sender.send(); // Send client move
receiver.receive(); // Receive server move
} while(!race.over());
s.close(); // Close connection
}
}
|
// Sender.java import java.net.*;
import java.io.*;
class Sender {
PrintStream out;
Race race;
public Sender(PrintStream out, Race race) {
this.out = out;
this.race = race;
}
public void send() {
char c='\0';
try {
do {
if(System.in.available() > 0) {
c=(char) System.in.read();
switch(c) {
case 'I': case 'J': case 'K': case 'M':
out.print(c);
race.moveme(c);
}
}
} while (c != 'I' && c != 'J' && c != 'K' && c != 'M' && !race.over());
}
catch(Exception e) { }
}
}
|
// Receiver.java import java.net.*;
import java.io.*;
class Receiver {
BufferedReader in;
Race race;
public Receiver(BufferedReader in, Race race) {
this.in = in;
this.race = race;
}
public void receive() {
char c;
try {
c = (char) in.read();
switch(c) {
case 'I': case 'J': case 'K': case 'M':
race.movethem(c);
}
}
catch(Exception e) {}
}
}
|
The primary difference between this and the previous example is that racers move at any time, no turn taking required. The turn taking was necessary because reading a connection-oriented stream blocks execution until there are characters to read. Once the read of the connection-oriented stream starts to await the client racer's move there is no way to unblock to read the server move from the keyboard. There are ways around this problem such as checking the number of available characters before attempting to read the stream. However, the problem can be more simply solved by using two threads, one for reading the connection-oriented stream and another for reading the keyboard. Whichever move is read first is applied to the race.
// RacerThreadedServer.java - Use: java -cp . Racerserver
import java.net.*;
import java.io.*;
public class RacerThreadedServer {
public static void main(String args[]) throws Exception {
// Server IP and port
ServerSocket connection = new ServerSocket( 888 );
Socket s = connection.accept(); // Wait for connection
BufferedReader in = new BufferedReader( // Buffered socket I/O
new InputStreamReader( s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
Race race = new Race('S', 2, 2, 'C', 0, 0); // Start race
ReceiverThread receiver = new ReceiverThread(in, race);
SenderThread sender = new SenderThread(out, race);
}
}
|
// RacerThreadedClient.java - Use: java -cp . Racerclient <IP or DNS>
import java.net.*;
import java.io.*;
public class RacerThreadedClient {
public static void main(String args[]) throws Exception {
// Server IP and port
Socket s = new Socket( (args.length == 0) ? "localhost" : args[0], 888 );
BufferedReader in = new BufferedReader( // Buffered socket I/O
new InputStreamReader( s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
Race race = new Race('C', 0, 0, 'S', 2, 2); // Start of race
ReceiverThread receiver = new ReceiverThread(in, race);
SenderThread sender = new SenderThread(out, race);
}
}
|
// SenderThread.java
import java.net.*;
import java.io.*;
class SenderThread implements Runnable {
PrintStream out;
Race race;
public SenderThread(PrintStream out, Race race) {
this.out = out;
this.race = race;
new Thread(this).start();
}
public void run() {
while(!race.over()) send();
out.close();
}
public void send() {
char c;
try { if(System.in.available() > 0) {
c=(char) System.in.read();
switch(c) {
case 'I': case 'J': case 'K': case 'M':
out.print(c);
race.moveme(c);
}
}
}
catch(Exception e) { }
}
}
|
// ReceiverThread.java
import java.net.*;
import java.io.*;
class ReceiverThread implements Runnable {
BufferedReader in;
Race race;
public ReceiverThread(BufferedReader in, Race race) {
this.in = in;
this.race = race;
new Thread(this).start();
}
public void run() {
while(!race.over()) receive();
}
public void receive() {
char c;
try { c = (char) in.read();
switch(c) {
case 'I': case 'J': case 'K': case 'M':
race.movethem(c);
}
}
catch(Exception e) { }
}
}
|
Race - The Race class implements a racing game where two opponents
attempt to crash into the other. Each racer moves in one of four directions from
an initial starting position. The game state is represented as a position on a
20x20 grid for each of two racers. A single character is printed on the 20x20
grid for each player after each move until a collision. The racer causing the
collision is declared the winner.
Note the use of synchronized for the moveme and movethem methods. The effect is when either of the methods is calls, a exclusive access lock is placed on the instance data of the Race object. If the moveme method is being executed, the movethem method is forced to wait until moveme method completes. Because the program using two threads, the ReceiverThread which calls movethem and SenderThread which calls moveme. Since common data is modified in both methods, it is possible for each method to corrupt the modifications of the other. Exclusive access of the Race object by each method prevents this form of data corruption. This is sometimes called serialization since the threads that normally execute in parallel are forced into serial execution when accessing common data.
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 != '?';
}
}
|
Computers are examples of state machines. State machines are simple machines that:
Maintaining state in a program is generally critical to performing useful work. For example, the following requires that variable A keep its value or state between transition from the state when A = 4 to the new state where A = A + 1:
A = 4
A = A + 1
Servers may maintain state of a client or not between messages. HTTP servers typically are termed stateless because each file request from a browser is treated independently from any previous request. The server receives and processs a request from the browser for a file download and forgets about the browser request immediately after outputting the file.
The Racer game is an example of a stateful server which must keep track of the state of the race game as the position of each player on the 20x20 grid. The race state is maintained by the Race object. It is important to note that the client/server are connected for the duration of the race and the Race object exists to maintain race state for the duration of the connection.
Maintaining state for a server is relatively straightforward when only a single client connection must be handled at a time. When multiple client connections must be handled simultaneously, care must be taken that the state of each client connection is maintained separately. The following example illustrates a race server that can support an arbitary number of race clients, note that the client programs remain unchanged. The key to maintaining separate state for each client is to create a set of new objects for each client connection so that each client has its own set of data.
The new server supports multiple client connections, maintaining the state of each race in separate objects for:
The server waits for a client connection at the connection.accept( )
statement. When a client connection is made the objects necessary for the race
state are
created. A
very important point is that new threads are started for handling each new
client connection independent of all other clients. Not only does each client
have separate objects for maintaining state, each client also has separate
threads for handling the connection input and output. The server maintains two
separate connections with two clients in the figure at right.
// MultiConnectionRaceServer.java - Use: java -cp . MultiConnectionRaceServer
import java.net.*;
import java.io.*;
public class MultiConnectionRaceServer {
public static void main(String args[]) throws Exception {
// Server IP and port
ServerSocket connection = new ServerSocket( 888 );
while (true) {
Socket s = connection.accept(); // Wait for connection
BufferedReader in = new BufferedReader( // Buffered socket input and output
new InputStreamReader( s.getInputStream() ) );
PrintStream out = new PrintStream(s.getOutputStream());
Race race = new Race('S', 2, 2, 'C', 0, 0); // Start race
ReceiverThread receiver = new ReceiverThread(in, race);
SenderThread sender = new SenderThread(out, race);
}
}
}
|
The following files may be downloaded by clicking on the name:
Document last modified: