Sign in to follow this  
Fixxer

Winsock: Multiple Connections.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 loop
enum SERVER_STATE { STATE_INIT,
STATE_RUN,
STATE_END
};

// Information about each connected client is stored here
struct 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 writing
fd_set clientsRead;


// State variables
bool keepRunning;
SERVER_STATE currentState;

// Server socket information
SOCKET listeningSocket = 0;
SOCKADDR_IN serverInfo;
WORD sockVersion;
WSADATA wsaData;

// Function Protos
void Main();
void InitWS();
void ListenForConnections();
void SendRecieve();
void PollClients();
void AcceptDataFromClient( SOCKET clientSocket );
int IndexBySocket( SOCKET clientSocket );
void ParseRecievedData( char* buffer );

// Program entry point
int 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 here
void 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 socket
void 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

Share this post


Link to post
Share on other sites
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 9411

class 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 maxfd
maxfd=0;

#ifdef _WIN32
WSADATA wsaData;
rtn=WSAStartup(MAKEWORD(1,1),&wsaData);
if (rtn!=0){
// Post error!?!
}


#endif

on_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 _WIN32
if (c->fd!=0){
#endif
FD_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-pend
rbuf.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);
#endif

rbuf=rbuf+buf;
free(buf);
#ifdef _WIN32
if (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();
}
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this