Created
December 23, 2018 16:30
-
-
Save codehoose/49f206c7253dc9b4507b56f25d18c2e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <WS2tcpip.h> | |
#include <string> | |
#include <sstream> | |
#pragma comment (lib, "ws2_32.lib") | |
using namespace std; | |
void main() | |
{ | |
// Initialze winsock | |
WSADATA wsData; | |
WORD ver = MAKEWORD(2, 2); | |
int wsOk = WSAStartup(ver, &wsData); | |
if (wsOk != 0) | |
{ | |
cerr << "Can't Initialize winsock! Quitting" << endl; | |
return; | |
} | |
// Create a socket | |
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0); | |
if (listening == INVALID_SOCKET) | |
{ | |
cerr << "Can't create a socket! Quitting" << endl; | |
return; | |
} | |
// Bind the ip address and port to a socket | |
sockaddr_in hint; | |
hint.sin_family = AF_INET; | |
hint.sin_port = htons(54000); | |
hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton .... | |
bind(listening, (sockaddr*)&hint, sizeof(hint)); | |
// Tell Winsock the socket is for listening | |
listen(listening, SOMAXCONN); | |
// Create the master file descriptor set and zero it | |
fd_set master; | |
FD_ZERO(&master); | |
// Add our first socket that we're interested in interacting with; the listening socket! | |
// It's important that this socket is added for our server or else we won't 'hear' incoming | |
// connections | |
FD_SET(listening, &master); | |
// this will be changed by the \quit command (see below, bonus not in video!) | |
bool running = true; | |
while (running) | |
{ | |
// Make a copy of the master file descriptor set, this is SUPER important because | |
// the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that | |
// are accepting inbound connection requests OR messages. | |
// E.g. You have a server and it's master file descriptor set contains 5 items; | |
// the listening socket and four clients. When you pass this set into select(), | |
// only the sockets that are interacting with the server are returned. Let's say | |
// only one client is sending a message at that time. The contents of 'copy' will | |
// be one socket. You will have LOST all the other sockets. | |
// SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!! | |
fd_set copy = master; | |
// See who's talking to us | |
int socketCount = select(0, ©, nullptr, nullptr, nullptr); | |
// Loop through all the current connections / potential connect | |
for (int i = 0; i < socketCount; i++) | |
{ | |
// Makes things easy for us doing this assignment | |
SOCKET sock = copy.fd_array[i]; | |
// Is it an inbound communication? | |
if (sock == listening) | |
{ | |
// Accept a new connection | |
SOCKET client = accept(listening, nullptr, nullptr); | |
// Add the new connection to the list of connected clients | |
FD_SET(client, &master); | |
// Send a welcome message to the connected client | |
string welcomeMsg = "Welcome to the Awesome Chat Server!\r\n"; | |
send(client, welcomeMsg.c_str(), welcomeMsg.size() + 1, 0); | |
} | |
else // It's an inbound message | |
{ | |
char buf[4096]; | |
ZeroMemory(buf, 4096); | |
// Receive message | |
int bytesIn = recv(sock, buf, 4096, 0); | |
if (bytesIn <= 0) | |
{ | |
// Drop the client | |
closesocket(sock); | |
FD_CLR(sock, &master); | |
} | |
else | |
{ | |
// Check to see if it's a command. \quit kills the server | |
if (buf[0] == '\\') | |
{ | |
// Is the command quit? | |
string cmd = string(buf, bytesIn); | |
if (cmd == "\\quit") | |
{ | |
running = false; | |
break; | |
} | |
// Unknown command | |
continue; | |
} | |
// Send message to other clients, and definiately NOT the listening socket | |
for (int i = 0; i < master.fd_count; i++) | |
{ | |
SOCKET outSock = master.fd_array[i]; | |
if (outSock != listening && outSock != sock) | |
{ | |
ostringstream ss; | |
ss << "SOCKET #" << sock << ": " << buf << "\r\n"; | |
string strOut = ss.str(); | |
send(outSock, strOut.c_str(), strOut.size() + 1, 0); | |
} | |
} | |
} | |
} | |
} | |
} | |
// Remove the listening socket from the master file descriptor set and close it | |
// to prevent anyone else trying to connect. | |
FD_CLR(listening, &master); | |
closesocket(listening); | |
// Message to let users know what's happening. | |
string msg = "Server is shutting down. Goodbye\r\n"; | |
while (master.fd_count > 0) | |
{ | |
// Get the socket number | |
SOCKET sock = master.fd_array[0]; | |
// Send the goodbye message | |
send(sock, msg.c_str(), msg.size() + 1, 0); | |
// Remove it from the master file list and close the socket | |
FD_CLR(sock, &master); | |
closesocket(sock); | |
} | |
// Cleanup winsock | |
WSACleanup(); | |
system("pause"); | |
} |
Kelly, don't you have to do a (lineno 143} FD_CLR(Listening, &Copy) also? Thanks
It will be fine because the copy is only used locally. Actually... there's probably no need to do FD_CLR(listening, &master);
either.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Kelly, don't you have to do a (lineno 143} FD_CLR(Listening, &Copy) also? Thanks