/////////////////////////////////////////////////////////// ///////////////// Have an itch? Scratch it! /////////////// ///////////////////////// SCRATCH ///////////////////////// ///////////////////// A MUD Server //////////////////// ///////////////////// By: Jared Devall //////////////////// ///////////////////// Thanks: //////////////////// ///////////////////// DIKU/Merc/ROM //////////////////// ///////////////////// Aetas/Deus Gang //////////////////// ///////////////////// Beej //////////////////// /////////////////////////////////////////////////////////// ///// If you see (WIN32), it is for Windows ONLY /////////////////////////////////////////////////////////// #if defined (WIN32) #include <winsock.h> #define EWOULDBLOCK EAGAIN #else #define closesocket close #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/time.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #endif ///// These are general includes, they work on any platform /////////////////////////////////////////////////////////// #include <string> #include <iostream> // The STL include. #include <cstdarg> #include <cstdio> #include <cerrno> #include <fcntl.h> #include "socket.h" #include <time.h> #define BACKLOG 15 const int defaultPort = 2950; // If no port is specified at startup, we use this using namespace std; Socket::Socket( ) { _disconnected = false; _gotInput = false; } Socket::~Socket( ){ SocketServer::Instance()._socketList.remove( this ); SocketServer::Instance().KillSocket( this ); } int Socket::GetDescriptor( ) { return _sockfd; } void Socket::SetDescriptor( int desc ) { _sockfd = desc; } string Socket::GetSocketIP( ) { return _ipAddress; } void Socket::SetSocketIP( const string &ip ) { _ipAddress = ip; } string Socket::GetSocketHost( ) { return _host; } void Socket::SetSocketHost( const string &host ) { _host = host; } void Socket::SetDisconnected( bool value ) { _disconnected = value; } bool Socket::IsDisconnected( ) { return _disconnected; } void Socket::SetGotInput( bool value ) { _gotInput = value; } bool Socket::GotInput( ) { return _gotInput; } string Socket::GetInput( ) { return _input; } string Socket::GetOutput( ) { return _output; } void Socket::FlushOutput( ) { _output.erase(); } void Socket::FlushInput( ) { _gotInput = false; _input.erase(); } void Socket::SetInput( const string &input ) { _input = input; } void Socket::SetOutput( const string &output ) { _output = output; } void Socket::Send( char * message, ... ) { va_list args; char buf[MAX_BUFFER]; string buf2; va_start( args, message ); vsprintf( buf, message, args); buf2 = buf; _output.append( buf2 ); va_end( args ); } void Socket::Send( const std::string &message ) { _output.append( message ); } ////////////////////////////// socket server ///////////////// SocketServer::SocketServer( ) { _defaultPort = defaultPort; _newConnection = false; #if defined WIN32 InitWinsock(); // Get our Sockets up and Running. #endif } SocketServer::~SocketServer( ) { #if defined WIN32 DeInitWinsock(); // Shutdown winsock #endif } #if defined WIN32 // Initializes Windows' Sockets void SocketServer::InitWinsock( ) { WSADATA wsaData; if ( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 ) { fprintf( stderr, "WSAStartup failed.\n" ); exit( 1 ); } } void SocketServer::DeInitWinsock( ) { WSACleanup(); } #endif bool SocketServer::CreateSocket( ) { // Let's Initialize a socket for the server if ( ( _sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { perror( "CreateSocket::socket " ); return false; } return true; } bool SocketServer::ReuseAddress( ) { int yes = 1; // Makes sure we can re-use the port if part of a socket is still lingering. // No "Address Already In Use" message. if ( setsockopt( _sockfd, SOL_SOCKET, SO_REUSEADDR, ( const char * )&yes, sizeof(int) ) == -1 ) { perror( "ReuseAddress::setsockopt " ); // Give us the error. return false; } return true; } // Binds server to specific port bool SocketServer::BindSocket( int port ) { sockaddr_in my_addr; if ( ( port < 1024 ) || ( 65535 < port ) ) { cout << "Port " << port << " out of range.\n"; cout << "Defaulting to port" << _defaultPort << endl; port = _defaultPort; // You can change this to default to whatever you want. } if ( !ReuseAddress() ) return false; my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons( port ); // short, network byte order my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); // auto-fill with my IP memset( &( my_addr.sin_zero ), '\0', 8 ); // zero the rest of the struct // Bind the server to port so people can connect to a certain port. if ( ( bind( _sockfd, ( struct sockaddr * )&my_addr, sizeof( struct sockaddr ) ) ) == -1 ) { perror( "BindSocket::bind " ); return false; } return true; } // Start listening on the port we binded to bool SocketServer::ListenSocket( ) { // Start listening for connections and other stuff. if ( listen( _sockfd, BACKLOG ) == -1 ) { perror( "listenSocket::listen " ); return false; } return true; } // Disconnects a specific socket void SocketServer::KillSocket( Socket * socket ) { shutdown( socket->GetDescriptor(), 2 ); // A cleaner close. closesocket( socket->GetDescriptor() ); // Close the Socket return; } // Goes through the socket lists and kills ones that have disconnected void SocketServer::KillDisconnectedSockets( ) { list< Socket * >::iterator sock; Socket *socket; for ( sock = _socketList.begin(); sock != _socketList.end(); ) { socket = (*sock); if ( socket->IsDisconnected() ) { sock = _socketList.erase( sock ); KillSocket( socket ); cout << socket->GetSocketIP() << " has disconnected.\n\r"; } else { ++sock; } } } // Creates new connection void SocketServer::ConceiveConnection( ) { char buf[MAX_BUFFER]; string buf2; //avatarMain * avatar; struct sockaddr_in sock; //struct hostent * from; Socket * socket; int sock_fd; int size; int addr; size = sizeof( struct sockaddr_in ); // I don't know why it does this, but I have to have Windows use plain &size and // linux cast &size to (socklen_t *) #if defined (WIN32) if ( ( sock_fd = accept( _sockfd, ( struct sockaddr* ) &sock, &size ) ) == -1 ) { perror( "ConceiveConnection::accept " ); return; } #else if ( ( sock_fd = accept( _sockfd, ( struct sockaddr* ) &sock, ( socklen_t* ) &size ) ) == -1 ) { perror( "ConceiveConnection::accept " ); return; } #endif // This'll get the IP address. xxx.xxx.xxx.xxx addr = ntohl( sock.sin_addr.s_addr ); sprintf( buf, "%d.%d.%d.%d", ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF, ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF ); cout << "New connection from: " << buf << endl; // This was producing some problems when testing, will have to get back to thread it // FIXME /* // This gets the 'name' of the host from it's address. i.e. bob@ ->jimmysinternet.com<- if ( ( from = gethostbyaddr( (char *) &sock.sin_addr, sizeof( sock.sin_addr ), AF_INET ) ) == NULL ) { cout << "System.error :: gethostbyaddr()" << endl; return; }*/ socket = new Socket; socket->SetDescriptor( sock_fd ); socket->SetSocketIP( buf ); // host is just the normal IP //desc->setUserIPlong( from ? from->h_name : buf ); // This is the "long-host", or name one. _socketList.push_back( socket ); // We have to add it to the descriptor List _newConnection = true; } // does the main work.. void SocketServer::ChaperoneConnections( ) { int stuff; list< Socket * >::iterator sock; // Live Descriptors int high = _sockfd; //clockStart = clock();// Check the time when we start the looping // This Zeroes or "Cleans" these fd sets. FD_ZERO( &_fd_read ); //FD_ZERO( &_fd_write ); FD_ZERO( &_fd_exc ); FD_SET( _sockfd, &_fd_read ); // Checks for sockets already in the list for ( sock = _socketList.begin(); sock != _socketList.end(); ++sock ) { if ( SendOutput( (*sock) ) != true ) { // This sends stuff to connections. (*sock)->SetDisconnected( true ); } if( (*sock)->GetDescriptor() > high ) high = (*sock)->GetDescriptor(); // This sets the descriptors in the fd sets. FD_SET( (*sock)->GetDescriptor(), &_fd_read ); //FD_SET( (*sock)->GetDescriptor(), &_fd_write ); FD_SET( (*sock)->GetDescriptor(), &_fd_exc ); } // select can be woken up by seeing a socket sending info // needing to recieve info, or throwing an exception. if ( select( high + 1, &_fd_read, NULL, &_fd_exc, 0 ) < 0 ){ perror("select: "); return; } // Let's create a new socket. if ( FD_ISSET( _sockfd, &_fd_read ) ) { ConceiveConnection(); } for ( sock = _socketList.begin(); sock != _socketList.end(); ++sock ) { // The socket had an accident. Let's kill it. if ( FD_ISSET( (*sock)->GetDescriptor(), &_fd_exc ) ) { (*sock)->SetDisconnected( true ); continue; } // Well, lets see what the socket can say! if( FD_ISSET( (*sock)->GetDescriptor(), &_fd_read ) ) { if ( (*sock)->GetDescriptor() == _sockfd ) { ConceiveConnection(); // Make a new Connection } else { // This was an older connection. if ( ( stuff = RecieveInput( (*sock) ) ) == Disconnected ) { // Recieve Input from a Connection (*sock)->SetDisconnected( true ); continue; } else if ( stuff == Complete ) { (*sock)->SetGotInput( true ); } } } } KillDisconnectedSockets(); return; } ///////// Name: listenConnection ///////// Args: Socket Descriptor that's sending info to server ///////// Does: Recieves input from a connection /////////////////////////////////////////////////////////////// int SocketServer::RecieveInput( Socket * socket ) { string::iterator cChar; string buf2; string input; long len = 0; long result = 0; char buf[MAX_BUFFER]; buf[0] = '\0'; strncpy( buf, "\0", sizeof( buf ) ); input = socket->GetInput(); if ( ( ( result = recv( socket->GetDescriptor(), buf + len, sizeof( buf ) - len, 0 ) ) <= 0 ) ) { if ( result == 0 ) { // nothing } else if ( errno == EWOULDBLOCK ) { input.append( buf ); socket->SetInput( input ); for ( cChar = input.begin(); cChar != input.end(); ++cChar ) { if ( (*cChar) == '\n' || (*cChar) == '\r' ) { return Complete; } } return Incomplete; } else { perror( "RecieveInput::recv " ); } return Disconnected; } len += result; // This removes a Backspace or Delete. if ( buf[len-1] == 8 || buf[len-1] == 127 ) { if ( strlen( buf ) > 0) { buf[len-1] = '\0'; buf[len-2] = '\0'; len -= 2; } else { buf[len-1] = '\0'; len--; } } input.append( buf ); socket->SetInput( input ); for ( cChar = input.begin(); cChar != input.end(); ++cChar ) { if ( (*cChar) == '\n' || (*cChar) == '\r' ) { return Complete; } } return Incomplete; } ////////// Name: sendConnection ////////// Args: Socket Descriptor to send info to. ////////// Does: Sends Output to a Connection who has stuff in ////////// their outbuf. /////////////////////////////////////////////////////////////// bool SocketServer::SendOutput( Socket * socket ) { string buf; string buf2; if ( socket->GetOutput().empty() ) return true; buf = socket->GetOutput(); if ( send( socket->GetDescriptor(), buf.c_str(), buf.length(), 0 ) < 0 ) { perror( "SendOutput::send " ); return false; } socket->FlushOutput(); return true; } int SocketServer::GetHostSocket( ) { return _sockfd; } void SocketServer::SetHostSocket( int sockfd ) { _sockfd = sockfd; } void SocketServer::Start( ) { CreateSocket( ); // Open up our socket. BindSocket( _defaultPort ); // bind the socket to the port. ListenSocket( ); // Begin listening } void SocketServer::Start( int port ) { CreateSocket( ); // Open up our socket. BindSocket( port ); // bind the socket to the port. ListenSocket( ); // Begin listening } bool SocketServer::Monitor ( ) { ChaperoneConnections(); if ( _newConnection ) { _newConnection = false; return true; } return false; }