WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(iResult != NO_ERROR){
cout<<"Error at WSAStartup."<<endl;
}
else{
cout<<"WSAStartup Completed."<<endl;
}
SOCKET m_socket;
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(m_socket == INVALID_SOCKET){
cout<<"Error at socket."<<endl;
WSACleanup();
return;
}
else{
cout<<"Socket Completed."<<endl;
}
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("127.0.0.1");
service.sin_port = htons(27000);
if(bind(m_socket,(SOCKADDR*) &service,sizeof(service)) == SOCKET_ERROR){
cout<<"Error at bind."<<endl;
closesocket(m_socket);
return;
}
else{
cout<<"Bind Completed."<<endl;
}
if(listen(m_socket,8) == SOCKET_ERROR){
cout<<"Error listening on socket."<<endl;
}
else{
cout<<"\nServer is now online."<<endl;
}
SOCKET AcceptSocket;
//////////////////////////////////////////////////////////////////////////
// Main Program Loop
//////////////////////////
// This is the main application loop.
//////////////////////////////////////////////////////////////////////////
while(1){
AcceptSocket = accept(m_socket,NULL,NULL);
if(AcceptSocket != SOCKET_ERROR){
cout<<"New Connection."<<endl;
m_socket = AcceptSocket;
}
}
Winsock: Multiple Connections.
Will this code support multiple connections? I dont think it will, but it may since I put 8 in the max connections spot. If not, how to I add support for multiple connections?
No, it will not.
The Forum FAQ has several useful links on this very topic. This is Q1 in the faq.
Please go read Beej's Guide to Network Programming and the WinSock FAQ
Especially significant for you from Beej's guide are section 1.6 (working with WinSock) and 6.2 (Synchronous I/O Multiplexing, AKA multiple connections).
frob.
The Forum FAQ has several useful links on this very topic. This is Q1 in the faq.
Please go read Beej's Guide to Network Programming and the WinSock FAQ
Especially significant for you from Beej's guide are section 1.6 (working with WinSock) and 6.2 (Synchronous I/O Multiplexing, AKA multiple connections).
frob.
Hi,
I recently wrote a small WinSock server that can handle multiple connections. The concept is that you poll the server listening port and if that port has data waiting you make the connection using accept(). As you receive connections you store them some how, in this case an array, and then each frame you check all of you client connections for data being sent in.
This server is a mirror server so that any number of clients up to the limit will receive what they send.
If you ignore 'Error' calls, they just log an error to a file via another cpp file.
Hope this helps.
ace
I recently wrote a small WinSock server that can handle multiple connections. The concept is that you poll the server listening port and if that port has data waiting you make the connection using accept(). As you receive connections you store them some how, in this case an array, and then each frame you check all of you client connections for data being sent in.
This server is a mirror server so that any number of clients up to the limit will receive what they send.
// Turn off "conditional expression is constant" error produced by the FD_SET macro#pragma warning(disable: 4127)#include <winsock.h>#include <windows.h>#include <stdio.h>#include "log.h"// Some global defines#define MAX_CLIENTS 10#define LISTENING_PORT 8888#define MAX_BUFFER_SIZE 10000// States for the finite state machine main loopenum SERVER_STATE { STATE_INIT, STATE_RUN, STATE_END};// Information about each connected client is stored herestruct ClientInfo{ SOCKET cSocket; sockaddr_in cSockAddrInfo; char cBuffer[ MAX_BUFFER_SIZE ];} clients[ MAX_CLIENTS ];unsigned int numConnectedClients = 0;// Set to hold all clients to be tested for reading and writingfd_set clientsRead;// State variablesbool keepRunning;SERVER_STATE currentState;// Server socket informationSOCKET listeningSocket = 0;SOCKADDR_IN serverInfo;WORD sockVersion; WSADATA wsaData;// Function Protosvoid Main();void InitWS();void ListenForConnections();void SendRecieve();void PollClients();void AcceptDataFromClient( SOCKET clientSocket );int IndexBySocket( SOCKET clientSocket );void ParseRecievedData( char* buffer );// Program entry pointint main( int argc, char* argv ){ // Stop warning about unused formal parameters in .NET argc; argv; keepRunning = true; currentState = STATE_INIT; while( keepRunning ) { Main(); } return 0;}// Main for the main loop, all switching and state changing is done via herevoid Main(){ switch ( currentState ) { case STATE_INIT: { InitWS(); if ( InitLogger() == -1 ) { currentState = STATE_END; } currentState = STATE_RUN; break; } case STATE_RUN: { ListenForConnections(); SendRecieve(); break; } case STATE_END: { WSACleanup(); CloseLogger(); keepRunning = false; break; } }}// Initialise Winsock and the listening socketvoid InitWS(){ sockVersion = MAKEWORD(1, 1); // We'd like Winsock version 1.1 if ( WSAStartup(sockVersion, &wsaData) != 0 ) { Error( "WSAStartup() Failed!" ); currentState = STATE_END; return; } listeningSocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( listeningSocket == INVALID_SOCKET ) { Error( "socket() failed, invalid socket created!" ); currentState = STATE_END; return; } serverInfo.sin_family = AF_INET; serverInfo.sin_addr.s_addr = INADDR_ANY; serverInfo.sin_port = htons(LISTENING_PORT); if ( bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)) == SOCKET_ERROR ) { Error( "bind() failed!" ); currentState = STATE_END; return; } listen(listeningSocket, 10);}void ListenForConnections(){ // fd_set struct contains sockets to be tested by select() fd_set listenSet; // Put the listening socket in the fd_set structure FD_ZERO( &listenSet ); FD_SET( listeningSocket, &listenSet ); // Set up a time struct to indicate waiting interval on the socket timeval tv; tv.tv_sec = 0; tv.tv_usec = 100; if ( select( 0, &listenSet, NULL, NULL, &tv ) == SOCKET_ERROR ) { Error( "select() failed!" ); currentState = STATE_END; return; } if ( listenSet.fd_count > 0 ) { int size = sizeof( sockaddr ); clients[ numConnectedClients ].cSocket = accept( listeningSocket, (sockaddr*) &clients[ numConnectedClients ].cSockAddrInfo, (int*) &size ); printf("\n\n********NEW CLIENT********\n\n"); ++numConnectedClients; }}void PollClients(){ if ( numConnectedClients > 0 ) { FD_ZERO( &clientsRead ); // run through the clients and add them to the fd_Set ready to be tested for ( unsigned int iter = 0; iter < numConnectedClients; ++iter ) { FD_SET( clients[ iter ].cSocket, & clientsRead ); } // Set up a time struct to indicate waiting interval on the socket timeval tv; tv.tv_sec = 0; tv.tv_usec = 100; select( 0, &clientsRead, NULL, NULL, &tv ); }}void SendRecieve(){ // If there is data to be sent then do this first for ( unsigned int iter = 0; iter < numConnectedClients; ++iter ) { if ( strlen( clients[ iter ].cBuffer ) > 0 ) { send( clients[ iter ].cSocket, clients[ iter ].cBuffer,(int) strlen( clients[ iter ].cBuffer ), 0 ); printf( "Sent to %s: \t\t%s\n", inet_ntoa( clients[ iter ].cSockAddrInfo.sin_addr ), clients[ iter ].cBuffer ); strcpy( clients[ iter ].cBuffer, "" ); } } // next poll the clients for reading PollClients(); // loop through all clients that are ready to be read from and process the data' for ( iter = 0; iter < clientsRead.fd_count; ++iter ) { AcceptDataFromClient( clientsRead.fd_array[ iter ] ); }}void AcceptDataFromClient( SOCKET clientSocket ){ unsigned int index = IndexBySocket( clientSocket ); if ( index == -1 ) return; recv( clientSocket, clients[ index ].cBuffer, MAX_BUFFER_SIZE, 0 ); if ( strlen( clients[ index ].cBuffer ) > 0 ) { ParseRecievedData( clients[ index ].cBuffer ); printf( "%s said: \t\t%s\n", inet_ntoa( clients[ index ].cSockAddrInfo.sin_addr ), clients[ index ].cBuffer ); }}int IndexBySocket( SOCKET clientSocket ){ for ( unsigned int iter = 0; iter < numConnectedClients; ++iter ) { if ( clients[ iter ].cSocket == clientSocket ) return iter; } return -1;}void ParseRecievedData( char* buffer ){ buffer;}
If you ignore 'Error' calls, they just log an error to a file via another cpp file.
Hope this helps.
ace
It will I think, but not the way you want.
accept blocks, meaning it will simply sit there each time in the loop, and not allow anything else to go on.
Here's more code, if you're the sort that easily reads code; a little more OOP style than most others. Should work on both windows and Unixy machines.
rmsnetwork.h
rmsnetwork.cc
And for completeness, some example code, to do the same sort of mirroring as ace's example:
mirrorserver.cc
accept blocks, meaning it will simply sit there each time in the loop, and not allow anything else to go on.
Here's more code, if you're the sort that easily reads code; a little more OOP style than most others. Should work on both windows and Unixy machines.
rmsnetwork.h
#ifndef _RMSNETWORK_#define _RMSNETWORK_#include <iostream>#include <sstream>#include <string>#include <list>#ifdef _WIN32#include <winsock.h>#define socklen_t int#else#include <fcntl.h>#include <sys/socket.h>#include <netdb.h>#include <sys/types.h>#include <netinet/in.h>#include <unistd.h>#include <sys/time.h>#include <arpa/inet.h>#endif#define NETWORK_DEFAULT_PORT 9411class networking;using namespace std;class connection{protected: int fd; int ready; string addr; string rbuf; string sbuf; virtual int read(networking&)=0; virtual int send(networking&)=0; connection (int,string&); virtual ~connection (){ #ifndef _WIN32 close(fd); #else closesocket(fd); #endif }public: string getline(); void read(string&); void undoread(string&); void send(string&); void send(char *); string fetchaddr(){return(addr);} friend ostream& operator<<(ostream &o, const connection &c); friend class networking; virtual int mtu(){return(1536);}};class tcp_connection: public connection{protected: int read(networking&); int send(networking&); tcp_connection(int infd, string& inaddr):connection(infd,inaddr){} friend class tcp_listen_connection; friend class networking;};class tcp_listen_connection: public connection{protected: int read(networking&); int send(networking&){} void accept(networking&); tcp_listen_connection(int, string&, string &); friend class networking;};class console_connection: public connection{protected: int read(networking&); int send(networking &n){ cout << sbuf; sbuf.erase(); } console_connection(string c):connection(0,c){} friend class networking;}; class networking{private: // TODO: ban list. list<connection *> connections; fd_set master; void add(connection *); void remove(connection *); struct timeval *tv; int maxfd; void (*on_accept)(tcp_connection *); void (*on_disconnect)(tcp_connection *); friend void tcp_listen_connection::accept(networking&);public: ~networking (); networking (); connection *fdtocon (int); connection *addrtocon (int); int contofd (connection *); connection *connect (string); connection *enable_console (); void disconnect (connection *); void read(); void send(); int listen(int,string,string); int listen(int); bool is_banned(string){return(0);} struct timeval timeout(); void timeout(struct timeval &); void set_accept(void (*in)(tcp_connection *)){on_accept=in;} void set_disconnect(void (*in)(tcp_connection *)){on_disconnect=in;}};#endif
rmsnetwork.cc
#include "rmsnetwork.h"#include <limits>ostream& operator<<(ostream &o, const connection &c){//////o << "connection\nfd " << c.fd <<"\naddr "<<c.addr<<"\nend.\n";return(o);}int networking::listen(int queue,string listen_addr,string port)//// cut/paste, cut/paste. Listener setup.//{ unsigned long addr; int sockfd; int yes=1; struct sockaddr_in my_addr; tcp_listen_connection *listener; sockfd = socket(AF_INET, SOCK_STREAM, 0); my_addr.sin_family = AF_INET; /* host byte order */ my_addr.sin_port = htons(atoi(port.c_str())); /* short, network byte order */ //my_addr.sin_addr.s_addr = INADDR_ANY; my_addr.sin_addr.s_addr = inet_addr(listen_addr.c_str()); bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(int)) ; /* don't forget your error checking for bind(): */ if (-1==bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))){ // TODO: throw an error, or pass to object logger. // either way, return. return(0); } if (-1==::listen (sockfd,queue)){ // TODO: throw and quit. return(0); } // TODO: pass a logger to object //printf ("Server started at %s, listening at %i\n",inet_ntoa(my_addr.sin_addr),sockfd); // TODO-DONE?: create listen object. // Pass sockfd, addr. listener=new tcp_listen_connection(sockfd,port,listen_addr); add(listener); return(sockfd);}int networking::listen(int queue){//////string a,b;stringstream ss;a="0.0.0.0";ss << NETWORK_DEFAULT_PORT;b=ss.str();return(listen(queue,a,b));}connection *networking::connect(string inaddr){//// Connect to inaddr.//// Assume TCP for now.// TODO: split this to elsewhere.string::size_type x;string caddr;string cport;x=inaddr.rfind(":");if (x!=inaddr.npos && x!=inaddr.length()-1 && x!=inaddr.length()-1 && x!=0){ cport.assign(inaddr,x+1,inaddr.length()); caddr.assign(inaddr,0,x);}else{ x=inaddr.rfind(" "); if (x!=inaddr.npos && x!=inaddr.length()-1 && x!=inaddr.length()-1 && x!=0){ cport.assign(inaddr,x+1,inaddr.length()); caddr.assign(inaddr,0,x); }else{ caddr=inaddr; cport=NETWORK_DEFAULT_PORT; }}//cout << "debug connect:\nAddr: " << caddr << "\nport: " <<cport <<"\n";int sockfd;int rtn;struct sockaddr_in addy;struct hostent *hostvar;connection *c;sockfd=socket(AF_INET,SOCK_STREAM,0);// TODO: dns lookups.addy.sin_family = AF_INET; /* host byte order */addy.sin_port = htons(atoi(cport.c_str())); /* short, network byte order */addy.sin_addr.s_addr = inet_addr(caddr.c_str());bzero(&(addy.sin_zero), 8); /* zero the rest of the struct */rtn=::connect(sockfd, (struct sockaddr *)&addy,sizeof(struct sockaddr));if (rtn==-1){ // Error! // Throw a connect fail return(0);}c=new tcp_connection(sockfd,inaddr);add(c);return(c);}void networking::disconnect(connection *c){//////close(c->fd);}connection *networking::enable_console(){//// Add console to connection list if it's not already there.//connection *c;list<connection *>::iterator lit;console_connection *cc;string cname("*console*");for (lit=connections.begin();lit!=connections.end();++lit){ c=*lit; if (c->fd==0){ return(c); }}cc=new console_connection(cname);add(cc);return(cc);}void networking_nothing(tcp_connection *in){} networking::networking(){//// Networking constructor.//int rtn;// Set tv to defaults.// default to non-blocking.tv=new timeval();tv->tv_sec=0;tv->tv_usec=0;// Set maxfdmaxfd=0;#ifdef _WIN32WSADATA wsaData;rtn=WSAStartup(MAKEWORD(1,1),&wsaData);if (rtn!=0){ // Post error!?!}#endifon_accept=networking_nothing;on_disconnect=networking_nothing;}networking::~networking(){//////list<connection *>::iterator cit;for (cit=connections.begin();cit!=connections.end();++cit){ delete *cit;}}void networking::add(connection *c){//// Add connection to list.//// TODO: retrieve dupes, and remove old?connections.push_back(c);#ifdef _WIN32if (c->fd!=0){#endifFD_SET(c->fd,&master);if (c->fd>maxfd){maxfd=c->fd;}#ifdef _WIN32}#endif}void networking::read(){//// Run select, set ready, call reads.//fd_set tmp;int rtn;connection *c;list<connection *>::iterator cit;list<connection *>::iterator e=connections.end();if (connections.empty()){return;}tmp=master;select(maxfd+1,&tmp,0,0,tv);for (cit=connections.begin();cit!=e;++cit){ c=*cit; if (c->ready!=-1){ #ifdef _WIN32 if (c->fd==0){ c->ready=1; }else{ #endif if (FD_ISSET(c->fd,&tmp)){ c->ready=1; }else{ c->ready=0; } #ifdef _WIN32 } #endif c->read(*this); }}}void networking::send(){//// Eerily similar to read...//fd_set tmp;int rtn;connection *c;list<connection *>::iterator cit;list<connection *>::iterator e=connections.end();if (connections.empty()){return;}tmp=master;select(maxfd+1,0,&tmp,0,tv);for (cit=connections.begin();cit!=e;++cit){ c=*cit; // TODO: change this if c is a windows stdin. if (c->ready!=-1){ if (FD_ISSET(c->fd,&tmp)){ c->ready=1; }else{ c->ready=0; } c->send(*this); }}}int networking::contofd(connection *c){//// Return copy of fd from connection.//return(c->fd);} connection::connection(int sock,string &inaddr){//////ready=0;fd=sock;addr=inaddr;//sbuf="moo";}string connection::getline(){//////string::size_type x;string rtn;if (rbuf.empty()){return(rtn);}x=rbuf.find_first_of("\r\n");rtn=rbuf.substr(0,x);if (x!=rbuf.length()){ x=rbuf.find_first_not_of("\r\n",x); if (x!=rbuf.npos){ rbuf=rbuf.substr(x,rbuf.length()); }else{ rbuf.clear(); }}else{ rbuf.clear();}return(rtn);}void connection::send(string &in){//////sbuf=sbuf+in;}void connection::send(char *in){//////string tmp=in;send(tmp);}void connection::read(string &out){//// Read 1 line into string//out=connection::getline();}void connection::undoread(string &in){//// Undo a read();//if (in[in.length()]!='\n'){ in=in+"\n";}rbuf=in+rbuf;}int tcp_connection::read(networking &n){//// Read data from socket if ready//string s;char *buf;int len=mtu();int rtn;if (ready!=1){ return(0);}buf=(char *)malloc(len);bzero(buf,len);rtn=recv(fd,buf,len,0);if (rtn<1){ // disconnection! // TODO: toss some sort of on-d/c // but for now, just unready. n.on_disconnect(this); ready=-1; free(buf); return(rtn);}// TODO: see if string has a pre-pendrbuf.append(buf);//cout << "in tcp::read(): " << fd << ":" << addr << ":" << rtn << ": " << buf << " -> " << rbuf << "\n";free(buf);return(rtn);}int tcp_connection::send(networking &n){//// Send data from socket if ready//int rtn;int len=mtu();int reallen;if (ready!=1){ return(0);}reallen=sbuf.length();if (reallen>len){ reallen=len;}rtn=::send(fd,sbuf.c_str(),reallen,0);if (rtn<0){ // disconnection! // TODO: toss a d/c. n.on_disconnect(this); ready=-1; return(rtn);}if (rtn){ sbuf.erase(0,rtn);}return(rtn);}tcp_listen_connection::tcp_listen_connection(int sock,string &port, string &inaddr):connection(sock,inaddr){//// Create listening socket connection object.//addr=inaddr +":"+port;}int tcp_listen_connection::read(networking &n){//// Check if ready. If so, send to accept!//if (ready==1){ accept(n); ready=0;}}void tcp_listen_connection::accept(networking &n){//// Yay, new connection. Accept it, and add to networking.//tcp_connection *tcpc;int newfd;int sinsize;struct sockaddr_in insock;string tmpaddr;newfd=::accept(fd,(struct sockaddr *)&insock,(socklen_t *)&sinsize);if (!newfd){ // Error. // Report it. return;}tmpaddr=inet_ntoa(insock.sin_addr);if (n.is_banned(tmpaddr)){ // IP banned. // TODO: post message to client? #ifndef _WIN32 close(newfd); #else closesocket(newfd); #endif return;}// New connection.// TODO: make a mechanism to post "new player" somewhere.tcpc=new tcp_connection(newfd,tmpaddr);n.add(tcpc);n.on_accept(tcpc);}int console_connection::read(networking &n){//// Read from kb.//char *buf;int len=mtu();unsigned long rtn;if (ready!=1){ return(0);}buf=(char *)malloc(len);bzero(buf,len);#ifdef _WIN32 ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE),buf,rtn-2,&bread,0);#else rtn=::read(fd,buf,len-2);#endifrbuf=rbuf+buf;free(buf);#ifdef _WIN32if (rtn>numeric_limits<int>::max()){ return(numeric_limits<int>::max());}else{#endif return(rtn);#ifdef _WIN32}#endif}
And for completeness, some example code, to do the same sort of mirroring as ace's example:
mirrorserver.cc
#include "rmsnetwork.h"#include <set>#include <string>using std::set;using std::string;networking network;set<connection *> clients;void add_client(tcp_connection *t){ clients.insert(t);}void rem_client(tcp_connection *t){ clients.erase(t);}void mirror(){set<connection *>::iterator it;string st;for(it=clients.begin();it!=clients.end();++it){ st=""; do{ st=(*it)->getline(); if(st.length()){ (*it)->send(st); (*it)->send("\n"); } }while(st.length()!=0);}}int main(){network.listen(10);network.set_accept(add_client);network.set_disconnect(rem_client);while(1){ network.read(); network.send(); mirror();}}
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement