Beruflich Dokumente
Kultur Dokumente
37
38 // Visual Studio .NET generated code
39
40 [STAThread]
41 static void Main()
42 {
43 Application.Run( new Server() );
44 }
45
46 protected void Server_Closing(
47 object sender, CancelEventArgs e )
48 {
49 System.Environment.Exit( System.Environment.ExitCode );
50 }
51
52 // sends the text typed at the server to the client
53 protected void inputTextBox_KeyDown(
54 object sender, KeyEventArgs e )
55 {
56 // sends the text to the client
57 try
58 {
59 if ( e.KeyCode == Keys.Enter && connection != null )
60 {
61 writer.Write( "SERVER>>> " + inputTextBox.Text );
62
63 displayTextBox.Text +=
64 "\r\nSERVER>>> " + inputTextBox.Text;
65
66 // if the user at the server signaled termination
67 // sever the connection to the client
68 if ( inputTextBox.Text == "TERMINATE" )
69 connection.Close();
70
71 inputTextBox.Clear();
72 }
73 }
74 catch ( SocketException )
75 {
76 displayTextBox.Text += "\nError writing object";
77 }
78 } // inputTextBox_KeyDown
79
80 // allows a client to connect and displays the text it sends
81 public void RunServer()
82 {
83 TcpListener listener;
84 int counter = 1;
85
86 // wait for a client connection and display the text
87 // that the client sends
88 try
89 {
143
144 // Step 5: close connection
145 inputTextBox.ReadOnly = true;
146 writer.Close();
147 reader.Close();
148 socketStream.Close();
149 connection.Close();
150
151 ++counter;
152 }
153 } // end try
154
155 catch ( Exception error )
156 {
157 MessageBox.Show( error.ToString() );
158 }
159
160 } // end method RunServer
161
162 } // end class Server
29
30 // default constructor
31 public Client()
32 {
33 InitializeComponent();
34
35 readThread = new Thread( new ThreadStart( RunClient ) );
36 readThread.Start();
37 }
38
39 // Visual Studio .NET-generated code
40
41 [STAThread]
42 static void Main()
43 {
44 Application.Run( new Client() );
45 }
46
47 protected void Client_Closing(
48 object sender, CancelEventArgs e )
49 {
50 System.Environment.Exit( System.Environment.ExitCode );
51 }
52
53 // sends text the user typed to server
54 protected void inputTextBox_KeyDown (
55 object sender, KeyEventArgs e )
56 {
57 try
58 {
59 if ( e.KeyCode == Keys.Enter )
60 {
61 writer.Write( "CLIENT>>> " + inputTextBox.Text );
62
63 displayTextBox.Text +=
64 "\r\nCLIENT>>> " + inputTextBox.Text;
65
66 inputTextBox.Clear();
67 }
68 }
69 catch ( SocketException ioe )
70 {
71 displayTextBox.Text += "\nError writing object";
72 }
73
74 } // end method inputTextBox_KeyDown
75
76 // connect to server and display server-generated text
77 public void RunClient()
78 {
79 TcpClient client;
80
As we analyze this example, we begin by discussing class Server (Fig. 19.1). In the
constructor, line 34 creates a Thread that will accept connections from clients. Line 35
starts the Thread, which invokes method RunServer (lines 81–160). Method Run-
Server initializes the server to receive connection requests and process connections. Line
91 instantiates the TcpListener to listen for a connection request from a client at port
5000 (Step 1). Line 94 then calls method Start of the TcpListener object, which
causes the TcpListener to begin waiting for requests (Step 2).
Lines 97–152 declare an infinite while loop that establishes connections requested
by clients (Step 3). Line 102 calls method AcceptSocket of the TcpListener object,
which returns a Socket upon successful connection. The thread in which method
AcceptSocket is called stops executing until a connection is established. The Socket
object will manage the connection. Line 105 passes this Socket object as an argument to
the constructor of a NetworkStream object. Class NetworkStream provides access
to streams across a network—in this example, the NetworkStream object provides
access to the Socket connection. Lines 108–109 create instances of the BinaryWriter
and BinaryReader classes for writing and reading data. We pass the Network-
Stream object as an argument to each constructor; BinaryWriter can write bytes to
the NetworkStream, and BinaryReader can read bytes from NetworkStream.
Lines 111–112 append text to the TextBox, indicating that a connection was received.
BinaryWriter method Write has many overloaded versions, which enable the
method to write various types to a stream. Line 115 uses method Write to send to the client
a string notifying the user of a successful connection. Lines 121–139 declare a do/while
structure that executes until the server receives a message indicating connection termination
(i.e., CLIENT>>> TERMINATE). Line 126 uses BinaryReader method ReadString
to read a string from the stream (Step 4). Method ReadString blocks until a string
is read. To prevent the whole server from blocking, we use a separate Thread to handle the
transfer of information. The while statement loops until there is more information to read—
this results in I/O blocking, which causes the program always to appear frozen. However, if
we run this portion of the program in a separate Thread, the user can interact with the Win-
dows Form and send messages while the program waits in the background for incoming mes-
sages.
When the chat is complete, lines 146–149 close the BinaryWriter, Bina-
ryReader, NetworkStream and Socket (Step 5) by invoking their respective
Close methods. The server then waits for another client connection request by returning
to the beginning of the while loop (line 97).
When the user of the server application enters a string in the TextBox and presses
the Enter key, event handler inputTextBox_KeyDown (lines 53–78) reads the
string and sends it via method Write of class BinaryWriter. If a user terminates
the server application, line 69 calls method Close of the Socket object to close the con-
nection.
Lines 46–50 define the Server_Closing event handler for the Closing event.
The event closes the application and uses System.Environment.Exit method with
parameter System.Environment.ExitCode to terminate all threads. Method Exit
of class Environment closes all threads associated with the application.
Figure 19.2 lists the code for the Client object. Like the Server object, the
Client object creates a Thread (lines 35–36) in its constructor to handle all incoming
messages. Client method RunClient (lines 77–137) connects to the Server,
receives data from the Server and sends data to the Server (when the user presses
Enter). Lines 87–88 instantiate a TcpClient object, then call its method Connect to
establish a connection (Step 1). The first argument to method Connect is the name of the
server—in our case, the server’s name is "localhost", meaning that the server is
located on the same machine as the client. The localhost is also known as the loopback
IP address and is equivalent to the IP address 127.0.0.1. This value sends the data trans-
mission back to the sender’s IP address. [Note: We chose to demonstrate the client/server
relationship by connecting between programs that are executing on the same computer
(localhost). Normally, this argument would contain the Internet address of another
computer.] The second argument to method Connect is the server port number. This
number must match the port number at which the server waits for connections.
The Client uses a NetworkStream to send data to and receive data from the server.
The client obtains the NetworkStream on line 91 through a call to TcpClient method
GetStream (Step 2). The do/while structure in lines 102–119 loops until the client
receives the connection-termination message (SERVER>>> TERMINATE). Line 109 uses
BinaryReader method ReadString to obtain the next message from the server (Step
3). Line 110 displays the message, and lines 124–127 close the BinaryWriter, Bina-
ryReader, NetworkStream and TcpClient objects (Step 4).
When the user of the client application enters a string in the TextBox and presses
the Enter key, the event handler inputTextBox_KeyDown (lines 54–74) reads the
string from the TextBox and sends it via BinaryWriter method Write. Notice
that, here, the Server receives a connection, processes it, closes it and waits for the next
one. In a real-world application, a server would likely receive a connection, set up the con-
nection to be processed as a separate thread of execution and wait for new connections. The
separate threads that process existing connections can continue to execute while the
Server concentrates on new connection requests.