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
|
#include <winsock2.h>
#include <iostream>
using namespace std;
/*
* This is our "server" socket; the one which is used to enable clients to connect to us.
*
*/
SOCKET hServerSocket;
/*
* And this is an array of sockets which will be used to store each connected client's socket.
*
* Keep in mind that a "SOCKET" is just an integer; when you create a socket you get a valid
* integer value. Given this fact, we can indicate an invalid client SOCKET by using a value
* that will never be returned which is value 0 (aka: NULL)
*
*/
SOCKET hClients[5];
void main()
{
WSADATA WI;
SOCKADDR_IN Addr;
u_long opt;
SOCKET hNewClientSocket;
char strChat[512];
int iResult;
/*
* First step as usual is to initialize Winsock.
*
*/
WSAStartup(0x2020, &WI);
/*
* Next, we must create the socket that we will receive client connections via.
*
*/
hServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/*
* Unlike the client, this time we're defining the address of the local network interface; the most significant
* aspect of this address is the port number.
*
*/
Addr.sin_family = AF_INET; // Again, IP address type.
Addr.sin_port = htons(6112); // We'll be receiving connections via port 6112 (this behaviour is referred to as "listening")
Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // And we'll just use loopback which is the easy way of saying "this computer"
/*
* Now all we have to do is associate the server socket with the address we defined above; this is accomplished using
* the bind() function.
*
*/
bind(hServerSocket, (SOCKADDR*) &Addr, sizeof(Addr));
/*
* Last of all we must set the server socket into "listening" mode which will enable it to receive connections
* from clients.
*
*/
listen(hServerSocket, SOMAXCONN);
/*
* Any socket can operate in one or two modes; blocking (synchronous) or non-blocking (asynchronous), by default
* all sockets operate in synchronous mode.
*
* In this program though, this is not suitable because we need to be capable of servicing multiple sockets (the n
* number of clients)
*
* The following function sets the socket mode to asynchronous.
*
*/
ioctlsocket(hServerSocket, FIONBIO, &(opt=1));
/*
* Now we begin the primary job which is accepting new client connections and also processing data that has
* been received from preexisting client sockets.
*
*/
for(;;)
{
/*
* First we'll see if a new connection has arrived; when a client calls connect() as is done within
* the partner program (ChatClient) a corrosponding accept() is performed on the server. This function
* will return a socket object that holds the connection to the new client.
*
*/
hNewClientSocket = accept(hServerSocket, NULL, NULL);
/*
* If a connection was available and it was successfully accepted then the return value (which was placed
* in the variable hSocket) will contain a value that is NOT equal to INVALID_SOCKET
*
*/
if(hNewClientSocket != INVALID_SOCKET)
{
/*
* As mentioned above, all our sockets must operate in asynchronous mode so we'll set that mode now
* on the new client's socket.
*
*/
ioctlsocket(hNewClientSocket, FIONBIO, &(opt=1));
/*
* Now that we have a new client socket we need to store it somewhere; this is where the global array
* hClientss comes in. So that we don't overwrite any other socket that we've already accepted, we must
* look for an element of the array with value 0 which indicates it is 'unused'
*
*/
for(int i = 0; i < 5; i++)
{
if(hClients[i] == 0)
{
/*
* Store the new client socket in the array element we found to be unused.
*
*/
hClients[i] = hNewClientSocket;
/*
* This is just some debug information output.
*
*/
cout << "\nOpen connection; ID=" << i;
/*
* The "break" keyword enables you to exit a loop prematurely. We're doing this because we
* have already stored the new client socket; if we kept looping then we'd end up utilizing
* the entire hClients array (because each one is by default, unused)
*
*/
break;
}
}
/*
* Send the client some sort of startup message.
*
*/
send(hNewClientSocket, "<Server> I've got a lovely bunch of coconuts, fiddely-dee.", 61, NULL);
}
/*
* Other than accepting new clients we must process data that has been sent by any of the clients. As this
* is a chat program, the job of the server is to broadcast chat messages it receives to everyone else whom
* is chatting (ie., all other clients.
*
* We'll loop through our array of client sockets and deal with them one by one remembering that a value of
* 0 for a client socket means it is unused.
*
*/
for(int i = 0; i < 5; i++)
{
/*
* You can use the 'continue' keyword to prematurely restart the loop; we'll do this to skip unused
* client sockets we come across.
*
*/
if(hClients[i] == 0) continue;
/*
* Now we'll receive any data that the client has sent us which will be copied into the strChat array.
* The return value of this function is meaningful, which will be sorted in a moment.
*
*/
iResult = recv(hClients[i], strChat, 512, NULL);
/*
* If recv() returns a value of SOCKET_ERROR then this indicates that there was outstanding data
* sent from the client.
*
*/
if(iResult != SOCKET_ERROR) // "If data HAS been received from the client; do the following"
{
/*
* There is another case where recv() returns the value 0, this indicates that the client has
* closed the connection (disconnected) and therefor what we need to do is destroy the socket
* we've been using for them and also set the element of hClientss to unused so that it can
* be reused for another client at some other point in time.
*
*/
if(iResult == 0)
{
closesocket(hClients[i]);
hClients[i] = 0;
/*
* This is just some debug information output.
*
*/
cout << "\nClose connection; ID=" << i;
}
/*
* The alternate scenario is that we successfully received data, this being the most common
* case.
*
*/
else // "If the client did NOT close the connection AND we received data then do the following"
{
/*
* Now all we must do is send the chat message to every other client who is currently
* connected to the server.
*
* IMPORTANT: Obviously we do not want to send it to ourselves aswell as that would be
* quite redundant.
*
*/
for(int x = 0; x < 5; x++)
{
if(x != i && hClients[x] != 0) // "If this element is not the same as the client who we just received data from AND it is not unused then do the following"
{
send(hClients[x], strChat, iResult, NULL);
}
}
}
}
} // End of "for(int i = 5; i < 5; i++)"
} // End of "for(;;)"
} // End of main()
|