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
|
/*
NOTE: This is a basic chat server which allows users to connect to it and be able
to talk in a public chat room. To connect, enter the following at your DOS prompt:
telnet localhost 6789
Do this for each chat client. If you want your classmates to connect to your server
within the laboratory LAN, simply change the macro 'HOST' to the actual IP address
of your network interface. For example, if the IP address of your network interface
is 10.10.7.23, then he/she can connect to your server by entering,
telnet 10.10.7.23 6789
at his/her DOS prompt.
*/
#include <iostream>
#include "PassiveSocket.h" // Include header for passive socket object definition
#define HOST "127.0.0.1" // localhost
#define PORT 6789
#define VERSION 0.01
#define NAME_LEN 11 // including the null character
#define MAX_PACKET 163 // actual message is 160-char long + "\r\n\0"
#define MAX_CLIENTS 100
#define TIME_OUT 5 // in minutes
#define SERVER_FULL "The server is full at the moment. Try again later."
using namespace std;
void bcast(CActiveSocket **clients, int clientCount, char *data, int except);
void sendto(CActiveSocket *client, char *data);
char *new_nick(char names[MAX_CLIENTS][NAME_LEN], int clientCount);
int main()
{
// server socket
CPassiveSocket server;
// client sockets
CActiveSocket *clients[MAX_CLIENTS], *incoming;
// number of clients connected so far
int clientCount;
// nicknames of the connected clients
char names[MAX_CLIENTS][NAME_LEN];
// msg is the message received from client 'i' so far
char msg[MAX_CLIENTS][MAX_PACKET];
// time_of_last_send[i] stores the last time that the client 'i' has sent a message;
// this will be used to impose timeouts on idle clients
long int time_of_last_send[MAX_CLIENTS];
// temporary string variables
char tmpdata[MAX_PACKET+20];
char tmpnick[NAME_LEN];
// Initialize server socket
server.Initialize();
// necessary so our server won't block (i.e. pause and wait) for input
server.SetNonblocking();
// and make it listen at host HOST and port PORT
server.Listen((const uint8 *) HOST, PORT);
cout << "TALKER v" << VERSION << endl;
cout << "Address: " << HOST << " Port: " << PORT << endl;
// we have no clients so far
clientCount = 0;
cout << "Number of clients: " << clientCount << endl;
// while it's not the end of the world
while (1)
{
// accept an incoming connection
if ((incoming = server.Accept()) != NULL)
{
// see if we can accommodate one more client
if (clientCount < MAX_CLIENTS)
{
// take that incoming connection
clients[clientCount] = incoming;
// necessary so our client won't block (i.e. pause and wait) for input
clients[clientCount]->SetNonblocking();
// start with a default nick and
strcpy(names[clientCount], new_nick(names, clientCount));
// an empty message buffer
strcpy(msg[clientCount], "");
cout << names[clientCount] << " has joined." << endl;
// display prompt to client
sprintf(tmpdata, "Welcome %s!\n\r%s> ", names[clientCount], names[clientCount]);
sendto(clients[clientCount], tmpdata);
// inform other clients
sprintf(tmpdata, "\n\r%s has joined.\n\r", names[clientCount]);
bcast(clients, clientCount+1, tmpdata, clientCount);
// we have one more client in
clientCount++;
cout << "Number of clients: " << clientCount << endl;
} // if
else // maximum number of clients reached; reject connection
{
incoming->Send((const uint8 *) SERVER_FULL, strlen(SERVER_FULL));
incoming->Close();
} // else
} // if
// for each client, check if it is sending a message to the server
// concatenate the new message of the client with its previous message if there's any
for (int i=0; i < clientCount; i++)
if (clients[i]->Receive(MAX_PACKET) > 0)
{
// empty tmpdata
strcpy(tmpdata, "");
// now catenate 'tmpdata' with the new message
strncat(tmpdata, (const char *) clients[i]->GetData(), clients[i]->GetBytesReceived());
// and then append it to the previous;
// msg[i] is the message received from client 'i' so far
strcat(msg[i], tmpdata);
}
// now, process the message sent by each client
// we only process a message if it is terminated by "\r\n" (produced by pressing the ENTER key)
// else we accumulate the messages above until it is terminated by "\r\n"
for (int i = 0; i < clientCount; i++)
{
// if the message contains '\n', process it
if (strchr(msg[i],'\n')) {
// but remove first "\r\n" from msg[i] by putting a '\0' before them
msg[i][strlen(msg[i])-2] = '\0';
// see if msg[i] is not empty
if (strlen(msg[i]) > 0)
{
// do the actual processing of the message here:
// any msg[i] which starts with a '.' is considered to be a command
if (msg[i][0] != '.')
{
// output to server console
cout << names[i] << ": " << msg[i] << endl;
// broadcast to the clients
sprintf(tmpdata, "%s: %s\n\r", names[i], msg[i]);
bcast(clients, clientCount, tmpdata, -1);
} else // it's a command!
{
// .nick <nickname> - change nickname command
if (strncmp(msg[i], ".nick ", 6) == 0)
{
// copy new nick which is right after the space after .nick
strcpy(tmpnick, msg[i]+6);
cout << names[i] << " is now known as " << tmpnick << "." << endl;
// broadcast to all the rest of the clients,
sprintf(tmpdata, "\n\r%s is now known as %s.\n\r", names[i], tmpnick);
bcast(clients, clientCount, tmpdata, i);
// now, overwrite the old nick with the new one
strcpy(names[i], tmpnick);
}
else if (strncmp(msg[i], ".quit", 5) == 0)
{
// say 'Bye.' to disconnecting client
sendto(clients[i], "Bye.");
// and then inform the others
sprintf(tmpdata, "\n\r%s is now offline.\n\r", names[i]);
bcast(clients, clientCount, tmpdata, i);
// and finally, close the connection
clients[i]->Close();
cout << names[i] << " is now offline." << endl;
// DELETION OF THE CLIENT FROM THE LIST:
// now, we move all storage starting at client 'i+1'
// to client 'clientCount-1', overwriting client 'i'
for (int j = i; j < clientCount-1; j++)
{
clients[j] = clients[j+1];
strcpy(msg[j], msg[j+1]);
strcpy(names[j], names[j+1]);
}
// less one client
clientCount--;
cout << "Number of clients: " << clientCount << endl;
// finally, restart 'i' to the position where we
// performed the deletion to process the client
// we moved from 'i+1'
i--;
// bypass post processing of 'msg[i]' and
// the display of the '>' prompt below
goto after_deletion;
}
}
}
// after processing the message buffer, empty it!
strcpy(msg[i], "");
// and then display the prompt '>'
sprintf(tmpdata, "%s> ", names[i]);
clients[i]->Send((const uint8*) tmpdata, strlen(tmpdata));
after_deletion:;
} // if
} // for
} // while
// close server socket
server.Close();
return 0;
} // main
// broadcasts 'data' to all clients except 'except'.
// to include sender, call this function with 'except' set to -1.
void bcast(CActiveSocket **clients, int clientCount, char *data, int except)
{
for (int i = 0; i < clientCount; i++)
if (i != except)
clients[i]->Send((const uint8*) data, strlen(data));
}
void sendto(CActiveSocket *client, char *data)
{
client->Send((const uint8*) data, strlen(data));
}
char *new_nick(char names[MAX_CLIENTS][NAME_LEN], int clientCount)
{
char tmpnick1[NAME_LEN], tmpnick2[NAME_LEN];
int unique_nick;
char msg[MAX_CLIENTS][MAX_PACKET];
for (int i = 0; i < MAX_CLIENTS; i++)
{
if (strcpy(tmpnick1, "Guest"))
{
itoa(i, tmpnick2, MAX_CLIENTS);
strcat(tmpnick1, tmpnick2);
unique_nick = 1;
for (int j = 0; j < clientCount; j++)
if (strcmp(tmpnick1, names[j]) == 0)
unique_nick = 0;
if (unique_nick)
return tmpnick1;
else if (strncmp(msg[i], ".who", 4) == 0)
{
//displays all the login users
cout << "This is the list of online users:" << endl;
for (int j=0; j < clientCount; j++)
{
cout << names[j] <<endl;
}
}
}
}
}
END
|