/***************************************************************************
Name: selectchatsrv.cpp
Autor: www.c-worker.ch
Comments: deutsch/english
Beschreibung:
Zeigt wie man blocking calls umgehen kann indem man select verwendet.
Mit select() kann ein Server geschrieben werden welcher mehrere Clients handelt,
ohne mehrere Threads zu erzeugen.
Beim diesem Demo handelt es sich um einen kleinen Chat-Server der auf Port 1234
läuft. Der client dazu heisst selectchatclient.cpp
Description:
Show how to use select() to write a server which handles multiple clients in
just one thread. It's a little chat server which runs on port 1234, the client
is named selectchatclient.cpp
***************************************************************************/
// max. Anzahl Clients
// Max. number of clients
#define MAX_CLIENTS 100
// Der Standartwert für FD_SETSIZE ist 64, dieser kann aber verändert
// werden indem man FD_SETSIZE auf einen anderen Wert setzt bevor man
// winsock2.h includiert.
// FD_SETSIZE auf die max. Anzahl Clients setzten
// The default value of FD_SETSIZE is 64, which can be modified by
// defining FD_SETSIZE to another value before including Winsock2.h.
// set FD_SETSIZE to the max. number of clients
#define FD_SETSIZE MAX_CLIENTS
// includes...
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#include <conio.h>
// Stellt eine Verbindung mit einem Client dar
// Represents a connection with a Client
struct Connection {
Connection() {
used=false;
socket=INVALID_SOCKET;
}
void set(SOCKET s, SOCKADDR_IN addr) {
this->socket=s;
this->addr=addr;
this->used=true;
}
void erase() {
this->used=false;
this->socket=INVALID_SOCKET;
}
bool used; // connection benutzt ? / connection slot used ?
SOCKET socket; // socket
SOCKADDR_IN addr; // client addr
};
// clients
Connection clients[MAX_CLIENTS];
// Sucht den nächsten freien Platz im clients Array
// -1 = kein platz frei
// Searches the next free slot in the clients array
// -1 = no free slot
int getFreeClientSlot() {
for(int i=0;i<MAX_CLIENTS;i++) {
if(clients[i].used==false)
return i;
}
return -1;
}
// Sendet eine Nachricht an alle Clients
// Send's a Message to all clients
int sendToAllClients(char* msg) {
int rc,i;
for(i=0;i<MAX_CLIENTS;i++) {
if(!clients[i].used)
continue;
rc=send(clients[i].socket,msg,strlen(msg),0);
if(rc==SOCKET_ERROR) {
printf("Error: Sending to Client %d failed: %d\n",i,WSAGetLastError());
}
}
return 0;
}
// Startet Winsock und gibt einige Infos zur Version aus
// Starts winsock and dumps some infos about the version
int startWinsock() {
int rc;
WSADATA wsaData;
rc=WSAStartup(MAKEWORD(2,0),&wsaData);
printf("Return Code: %d\n",rc);
if(rc==SOCKET_ERROR) {
printf("Error, exiting!\n");
return rc;
}
printf("Winsock started:\n");
printf("Version: %d.%d\n",LOBYTE(wsaData.wVersion),HIBYTE(wsaData.wVersion));
printf("High Version: %d.%d\n",LOBYTE(wsaData.wHighVersion),HIBYTE(wsaData.wHighVersion));
printf("Description: %s\n",wsaData.szDescription);
printf("System Status: %s\n",wsaData.szSystemStatus);
return 0;
}
// main...
int main() {
// SOCKET welcher neue Verbindungen annimmt
// SOCKET which accepts new connections
SOCKET acceptSocket;
SOCKADDR_IN addr;
int rc,addrsize=sizeof(SOCKADDR_IN);
unsigned int i,j;
// fd_set
fd_set fdSetRead;
// timout für select() / timeout for select()
timeval selectTimeout;
// temporär benutz für neue verbindungen
// temporary used for new connections
Connection newConnection;
// buffer
char buf[1024];
// clients array leeren / clear clients array
memset(clients,0x0,sizeof(Connection)*MAX_CLIENTS);
// start winsock
rc=startWinsock();
if(rc==SOCKET_ERROR)
return 1;
// socket erstellen / create socket
acceptSocket=socket(PF_INET,SOCK_STREAM,0);
if(acceptSocket==INVALID_SOCKET) {
printf("Error, cannot create socket: %d\n",WSAGetLastError());
return 1;
}
// sockt an port 1234 binden / bind socket to port 1234
memset(&addr,0,sizeof(SOCKADDR_IN));
addr.sin_family=AF_INET;
addr.sin_port=htons(1234);
addr.sin_addr.s_addr=ADDR_ANY;
rc=bind(acceptSocket,(SOCKADDR*)&addr,sizeof(SOCKADDR_IN));
if(rc==SOCKET_ERROR) {
printf("Error, bind() failed: %d\n",WSAGetLastError());
return 1;
}
// auf verbindungen warten / listen for connections
rc=listen(acceptSocket,10);
if(rc==SOCKET_ERROR) {
printf("Error,listen() failed: %d\n",WSAGetLastError());
return 1;
}
// The parameter readfds identifies the sockets that are to be checked for readability.
// If the socket is currently in the listen state, it will be marked as readable if an
// incoming connection request has been received such that an accept is guaranteed to
// complete without blocking.
while(1) {
// fd_set leeren / clear fd_set
FD_ZERO(&fdSetRead);
// den socket welcher verbindungen annimmt hinzufügen
// add the SOCKET which accepts new connections
FD_SET(acceptSocket,&fdSetRead);
// alle clients hinzufügen
// add all clients
for(i=0;i<MAX_CLIENTS;i++) {
if(clients[i].used)
FD_SET(clients[i].socket,&fdSetRead);
}
// warten bis irgend ein socket bereit ist, wenn timout NULL ist kehrt select()
// erst zurück wenn ein socket bereit ist, select() blockt also in diesem falle
// wait until any socket is ready (timeout = NULL, block until one socket is ready)
rc=select(0,&fdSetRead,NULL,NULL,NULL);
// abbrechen bei einem fehler
// break on error
if(rc<1)
break;
printf("select() returned %d ready sockets\n",rc);
for(i=0;i<fdSetRead.fd_count;i++) {
// acceptSocket ?
if(fdSetRead.fd_array[i]==acceptSocket) {
// verbindung annehmen / accept new connection
newConnection.socket=accept(acceptSocket,(SOCKADDR*)&newConnection.addr,&addrsize);
rc=getFreeClientSlot();
if(rc==-1) {
printf("Cannot accept new clients\n");
continue;
}
// zu den clients hinzufügen
// add to clients
clients[rc]=newConnection;
clients[rc].used=true;
printf("New Client accepted from %s\n",inet_ntoa(newConnection.addr.sin_addr));
continue;
}
// ein client ?
// a client ?
for(j=0;j<MAX_CLIENTS;j++) {
if(!clients[j].used)
continue;
if(clients[j].socket==fdSetRead.fd_array[i]) {
rc=recv(clients[j].socket,buf,1023,0);
buf[rc]='\0';
// rc==0 => client hat die verbindung beendet
// rc==0 => client closed connection
if(rc==0) {
printf("Client %d (%s): closed connection\n",j,inet_ntoa(clients[j].addr.sin_addr));
closesocket(clients[j].socket);
clients[j].erase();
continue;
// rc==SOCKET_ERROR => fehler, verbindung beenden!
// rc==SOCKET_ERROR => error, close connection!
} else if(rc==SOCKET_ERROR) {
printf("Client %d (%s): Error %d\n",j,inet_ntoa(clients[j].addr.sin_addr),WSAGetLastError());
printf("Client %d (%s): Server aborts connection\n",j,inet_ntoa(clients[j].addr.sin_addr));
closesocket(clients[j].socket);
clients[j].erase();
continue;
// daten empfangen und an alle clients senden
// receive data and send it to all clients
} else {
printf("Client %d (%s): received '%s' \n",j,inet_ntoa(clients[j].addr.sin_addr),buf);
sendToAllClients(buf);
}
}
}
}
}
// aufräumen
// cleanup
closesocket(acceptSocket);
WSACleanup();
printf("Server shutdown, press any key to exit\n");
getch();
}