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