Code:
//select example
// by Steven Mills
// [email protected]
// Tested and compiled on dev c++. Compatibility with other compliers
// can't be guaranteed.
// use library ws2_32.lib with this project
#include <iostream>
#include <winsock2.h>
using namespace std;
// *** DECLARATIONS *** //
const int PORT = 80; // this is the port the server runs on
const int ireqver = 2; // required version of winsock on the machine running the server
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
struct sockaddr_in myaddr; //local machine's ip
struct sockaddr_in remoteaddr; //client ip
int fdmax; // maximum file descriptor number
int newfd; //newly accept()ed socket discriptor
char buf[256]; //buffer for client data
int nbytes;
int addrlen;
int i, j;
WSADATA wsaData;
char welcome[] = {"Welcome! \n Chat program 1.0 by Steven Mills \n"};
/// *** MAIN PROGRAM *** ///
int main(int argc, char *argv[])
{
FD_ZERO(&master); // clear the master
FD_ZERO(&read_fds); // and temp sets
cout << "initiating winsock...";
if (WSAStartup(MAKEWORD(ireqver, 0), &wsaData) == 0) // checking winsock version
{
if (LOBYTE(wsaData.wVersion) >= ireqver) // is version good enough?
{
cout << "initiated." << endl; // yes
}
}
else {
cout << "requested version not available." << endl; // no
}
//set up the listening socket!
cout << "setting up listening socket...";
SOCKET listener; // listening socket
listener = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // defining the socket
// PF_INET is the domain, or Protocol Family i selected.
// You can also use AF_INET (Adress Family) for some reason,
// the program works with both values, anyway, just remember
// that you have to have this, because in these families is where
// the TCP/IP and UDP connections are.
// SOCK_STREAM indicates that we are using streaming sockets (TCP), and not a
// datagram (UDP, or in the code it would be SOCK_DGRAM)
// type of socket, which means that the bits in our SOCK_STREAM socket will get there
// in the same order that they left. Datagram sockets are not suitable, for this
// program, as they are for speed and not accuracy. They are better used in things like
// streaming audio and video.
// IPPROTO_TCP, or TCP/IP Protocol. see it?
myaddr.sin_family = AF_INET; // assigning address family
myaddr.sin_addr.s_addr = INADDR_ANY; // telling the server to accept any ip
myaddr.sin_port = htons(PORT); // but only on this particular port
memset(&(myaddr.sin_zero), '\0', 8);
if (listener == SOCKET_ERROR) { // check for errors
cout << "failed." << endl;
}
else if (bind(listener, (struct sockaddr *) &myaddr, sizeof(myaddr)) != 0)
{ cout << "failed" << endl;
}
// starting the listening!
if (listen(listener, 10) != 0) { // a value of non-zero indicates an error
cout << "failed";
}
else
{ cout << "success." << endl;
}
//add the listener to the master set
FD_SET(listener, &master);
//keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
//main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { // select will handle
// the list of connections in the file descriptor
cout << "error with select"; // error checking
}
// this following loop goes through the existing connections
// looking for data to read, or new connections.
for (i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // NEW CONNECTION
if (i == listener) {
// handle new connection
addrlen = sizeof(remoteaddr);
if ((newfd = accept(listener, (struct sockaddr *)&remoteaddr,
&addrlen)) == -1) { // accepts new connection
cout << "error processing new connection"; // error checking
}
else {
FD_SET(newfd, &master); //add to master set
if (newfd > fdmax) {// keeping track of maximum
fdmax = newfd;
}
cout << "New connection from: " << inet_ntoa(remoteaddr.sin_addr) << " on socket "
<< newfd << "." << endl; // this identifies the connection
if (send(newfd, welcome, sizeof(welcome), 0) == -1) { // sends the user
// a welcome message when they connect to the server
cout << "error while sending welcome message" << endl; // error checking
}
}
}
else { // handle data from a client
if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
// error or connection terminated by client
if (nbytes == 0) { //indicates that the client disconnected
//connection closed
cout << "socket " << i << " disconnected" << endl;
} else {
cout << "error recieving data" << endl; // error checking
}
closesocket(i); // socket closed
FD_CLR(i, &master); // remove from master set
}
else { // real data from client
for (j = 0; j <= fdmax; j++) {
// send to every client
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
cout << "error while sending" << endl; // error checking
}
}
}
}
}
}
}
}
}
if (WSACleanup() != 0) { // have to clean up your winsock mess!
cout << "cleanup failed." << endl; // error checking
}
system("PAUSE"); // you can use these last two lines if you want
return EXIT_SUCCESS;
}
by the way, if anyone has any suggestions on improving the code, feel free.