Advertisement

Winsock: Multiple Connections.

Started by September 01, 2005 09:13 AM
3 comments, last by Telastyn 19 years, 5 months ago
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?

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;
		}
}

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.
Advertisement
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.

// 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
TY for the links.
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
#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