1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
| using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections;
namespace ChatServer
{
// Holds the arguments for the StatusChanged event
public class StatusChangedEventArgs : EventArgs
{
// The argument we're interested in is a message describing the event
private string EventMsg;
// Property for retrieving and setting the event message
public string EventMessage
{
get
{
return EventMsg;
}
set
{
EventMsg = value;
}
}
// Constructor for setting the event message
public StatusChangedEventArgs(string strEventMsg)
{
EventMsg = strEventMsg;
}
}
// This delegate is needed to specify the parameters we're passing with our event
public delegate void StatusChangedEventHandler(object sender, StatusChangedEventArgs e);
class ChatServer
{
// This hash table stores users and connections (browsable by user)
public static Hashtable htUsers = new Hashtable(30); // 30 users at one time limit
// This hash table stores connections and users (browsable by connection)
public static Hashtable htConnections = new Hashtable(30); // 30 users at one time limit
// Will store the IP address passed to it
private IPAddress ipAddress;
private TcpClient tcpClient;
// The event and its argument will notify the form when a user has connected, disconnected, send message, etc.
public static event StatusChangedEventHandler StatusChanged;
private static StatusChangedEventArgs e;
// The constructor sets the IP address to the one retrieved by the instantiating object
public ChatServer(IPAddress address)
{
ipAddress = address;
}
// The thread that will hold the connection listener
private Thread thrListener;
// The TCP object that listens for connections
private TcpListener tlsClient;
// Will tell the while loop to keep monitoring for connections
bool ServRunning = false;
// Add the user to the hash tables
public static void AddUser(TcpClient tcpUser, string strUsername)
{
// First add the username and associated connection to both hash tables
ChatServer.htUsers.Add(strUsername, tcpUser);
ChatServer.htConnections.Add(tcpUser, strUsername);
// Tell of the new connection to all other users and to the server form
SendAdminMessage(htConnections[tcpUser] + " has joined us");
}
// Remove the user from the hash tables
public static void RemoveUser(TcpClient tcpUser)
{
// If the user is there
if (htConnections[tcpUser] != null)
{
// First show the information and tell the other users about the disconnection
SendAdminMessage(htConnections[tcpUser] + " has left us");
// Remove the user from the hash table
ChatServer.htUsers.Remove(ChatServer.htConnections[tcpUser]);
ChatServer.htConnections.Remove(tcpUser);
}
}
// This is called when we want to raise the StatusChanged event
public static void OnStatusChanged(StatusChangedEventArgs e)
{
StatusChangedEventHandler statusHandler = StatusChanged;
if (statusHandler != null)
{
// Invoke the delegate
statusHandler(null, e);
}
}
// Send administrative messages
public static void SendAdminMessage(string Message)
{
StreamWriter swSenderSender;
// First of all, show in our application who says what
e = new StatusChangedEventArgs("Administrator: " + Message);
OnStatusChanged(e);
// Create an array of TCP clients, the size of the number of users we have
TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count];
// Copy the TcpClient objects into the array
ChatServer.htUsers.Values.CopyTo(tcpClients, 0);
// Loop through the list of TCP clients
for (int i = 0; i < tcpClients.Length; i++)
{
// Try sending a message to each
try
{
// If the message is blank or the connection is null, break out
if (Message.Trim() == "" || tcpClients[i] == null)
{
continue;
}
// Send the message to the current user in the loop
swSenderSender = new StreamWriter(tcpClients[i].GetStream());
swSenderSender.WriteLine("Administrator: " + Message);
swSenderSender.Flush();
swSenderSender = null;
}
catch // If there was a problem, the user is not there anymore, remove him
{
RemoveUser(tcpClients[i]);
}
}
}
// Send messages from one user to all the others
public static void SendMessage(string From, string Message)
{
StreamWriter swSenderSender;
// First of all, show in our application who says what
e = new StatusChangedEventArgs(From + " says: " + Message);
OnStatusChanged(e);
// Create an array of TCP clients, the size of the number of users we have
TcpClient[] tcpClients = new TcpClient[ChatServer.htUsers.Count];
// Copy the TcpClient objects into the array
ChatServer.htUsers.Values.CopyTo(tcpClients, 0);
// Loop through the list of TCP clients
for (int i = 0; i < tcpClients.Length; i++)
{
// Try sending a message to each
try
{
// If the message is blank or the connection is null, break out
if (Message.Trim() == "" || tcpClients[i] == null)
{
continue;
}
// Send the message to the current user in the loop
swSenderSender = new StreamWriter(tcpClients[i].GetStream());
swSenderSender.WriteLine(From + " says: " + Message);
swSenderSender.Flush();
swSenderSender = null;
}
catch // If there was a problem, the user is not there anymore, remove him
{
RemoveUser(tcpClients[i]);
}
}
}
public void StartListening()
{
// Get the IP of the first network device, however this can prove unreliable on certain configurations
IPAddress ipaLocal = ipAddress;
// Create the TCP listener object using the IP of the server and the specified port
tlsClient = new TcpListener(1986);
// Start the TCP listener and listen for connections
tlsClient.Start();
// The while loop will check for true in this before checking for connections
ServRunning = true;
// Start the new tread that hosts the listener
thrListener = new Thread(KeepListening);
thrListener.Start();
}
private void KeepListening()
{
// While the server is running
while (ServRunning == true)
{
// Accept a pending connection
tcpClient = tlsClient.AcceptTcpClient();
// Create a new instance of Connection
Connection newConnection = new Connection(tcpClient);
}
}
}
// This class handels connections; there will be as many instances of it as there will be connected users
class Connection
{
TcpClient tcpClient;
// The thread that will send information to the client
private Thread thrSender;
private StreamReader srReceiver;
private StreamWriter swSender;
private string currUser;
private string strResponse;
// The constructor of the class takes in a TCP connection
public Connection(TcpClient tcpCon)
{
tcpClient = tcpCon;
// The thread that accepts the client and awaits messages
thrSender = new Thread(AcceptClient);
// The thread calls the AcceptClient() method
thrSender.Start();
}
private void CloseConnection()
{
// Close the currently open objects
tcpClient.Close();
srReceiver.Close();
swSender.Close();
}
// Occures when a new client is accepted
private void AcceptClient()
{
srReceiver = new System.IO.StreamReader(tcpClient.GetStream());
swSender = new System.IO.StreamWriter(tcpClient.GetStream());
// Read the account information from the client
currUser = srReceiver.ReadLine();
// We got a response from the client
if (currUser != "")
{
// Store the user name in the hash table
if (ChatServer.htUsers.Contains(currUser) == true)
{
// 0 means not connected
swSender.WriteLine("0|This username already exists.");
swSender.Flush();
CloseConnection();
return;
}
else if (currUser == "Administrator")
{
// 0 means not connected
swSender.WriteLine("0|This username is reserved.");
swSender.Flush();
CloseConnection();
return;
}
else
{
// 1 means connected successfully
swSender.WriteLine("1");
swSender.Flush();
// Add the user to the hash tables and start listening for messages from him
ChatServer.AddUser(tcpClient, currUser);
}
}
else
{
CloseConnection();
return;
}
try
{
// Keep waiting for a message from the user
while ((strResponse = srReceiver.ReadLine()) != "")
{
// If it's invalid, remove the user
if (strResponse == null)
{
ChatServer.RemoveUser(tcpClient);
}
else
{
// Otherwise send the message to all the other users
ChatServer.SendMessage(currUser, strResponse);
}
}
}
catch
{
// If anything went wrong with this user, disconnect him
ChatServer.RemoveUser(tcpClient);
}
}
}
}
Форма сервера:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace ChatServer
{
public partial class Form1 : Form
{
private delegate void UpdateStatusCallback(string strMessage);
public Form1()
{
InitializeComponent();
}
private void btnListen_Click(object sender, EventArgs e)
{
// Parse the server's IP address out of the TextBox
IPAddress ipAddr = IPAddress.Parse(txtIp.Text);
// Create a new instance of the ChatServer object
ChatServer mainServer = new ChatServer(ipAddr);
// Hook the StatusChanged event handler to mainServer_StatusChanged
ChatServer.StatusChanged += new StatusChangedEventHandler(mainServer_StatusChanged);
// Start listening for connections
mainServer.StartListening();
// Show that we started to listen for connections
txtLog.AppendText("Monitoring for connections...\r\n");
}
public void mainServer_StatusChanged(object sender, StatusChangedEventArgs e)
{
// Call the method that updates the form
this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { e.EventMessage });
}
private void UpdateStatus(string strMessage)
{
// Updates the log with the message
txtLog.AppendText(strMessage + "\r\n");
}
}
} |