i see many people while trying to figure out winsock and networking often find basic blocking sockets easy to comprehend, but then get hopelessly stuck when it comes to using select(). now, this source code i wrote might not answer all the questions, but it at least has everything needed in a select server, and tries to explain what's happening. this program is a chat program, where multiple people can telnet in and see eachothers' messages:
by the way, if anyone has any suggestions on improving the code, feel free.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;
}