// Simple C++ sockets-based echo server, based on the public domain // echo server at the Great Programming Languages Shootout at // http://www.bagley.org/~doug/shootout/, but cleaned up a lot. // We're aiming for clarity, not low line count. // // Any of the bizarre sockets stuff can be looked up here: // http://unixhelp.ed.ac.uk/CGI/man-cgi?recv+2 // where recv is replaced with whichever function you want. // // There's another implementation worth looking at here: // http://www.disi.unige.it/person/GianuzziV/SysOp/lucidi/sos.html // (though lawyers can note that I looked at it after writing this) // // Compile with MinGW g++ via: // g++ echoserv.cpp -o echoserv.exe -lwsock32 // or Microsoft Visual C++ via: // cl echoserv.cpp // // This file is in the public domain, which means you can do whatever you // want with it without me hassling you. #include #include #include #include #include #include // in wsock32.lib // Suggest to the linker that it might want to include the correct library: // #ifdef _MSC_VER // Microsoft Visual C++ # pragma comment( lib, "wsock32.lib" ) #endif enum { PORT = 3344 }; // arbitrary //! This provides the whole "resource allocation is initialisation" idiom, //! which allows us to disconnect from WinSock cleanly if we ever stop serving. //! ...which we don't. Oh well. class SocketServices { public: SocketServices() : m_error_code(0), m_WsaData() { // Initialize sockets support enum { winsock_version = 0x0001 }; m_error_code = WSAStartup( winsock_version, &m_WsaData ); } ~SocketServices() { // Close sockets support. if (available()) WSACleanup(); } bool available() const { return m_error_code == 0; } int get_init_error() const { return m_error_code; } WSADATA const & winsock_data() const { return m_WsaData; } protected: int m_error_code; WSADATA m_WsaData; }; enum ErrorCodes { socket_error = -1, setsockopt_error = -2, bind_error = -3 }; int get_server_socket() { int endpoint = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if (endpoint == -1) return socket_error; // int optval = 1; // int sso = setsockopt( endpoint, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof(optval) ); // if (sso == -1) // return setsockopt_error; // htonl: "hostname-to-number lookup"; ascii-to-binary conversion // htons: "host-to-network-short"; endianness conversion sockaddr_in sin = {0}; sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); sin.sin_port = htons( PORT ); // echo port is 7, http is 80 int b = bind( endpoint, (sockaddr*)&sin, sizeof(sin) ); if (b == -1) return bind_error; listen( endpoint, 2 ); // allow a backlog of up to 2 waiting requests before refusing connections. return endpoint; } void serve_forever( int ssock ) { sockaddr_in sin = {0}; int slen = sizeof(sin); // socklen_t doesn't exist on Windows, and int is fine. printf( "Echo server serving on port %d... (answering this computer only)\n", PORT ); for(;;) // yes, really forever. { // Wait for a connection, assign it to socket descriptor csock. int csock = accept( ssock, (sockaddr *)&sin, &slen ); if ( csock == -1 ) { fprintf( stderr, "Couldn't accept client's connection.\n" ); continue; } printf( "Got client connection!\n" ); char buf[4 * 1024]; // used for both reading and writing int total_bytes = 0; int len = 0; while ( 1 ) { enum { send_flags = 0 }; enum { recv_flags = 0 }; // consider MSG_DONTWAIT for non-blocking reads, // but check the documentation first. len = recv( csock, buf, sizeof(buf), recv_flags ); if ( len <= 0 ) break; total_bytes += len; // Echo response back to client. char* offset = buf; while ( len > 0 ) { int written = send( csock, offset, len, send_flags ); if ( written == -1 ) { fprintf( stderr, "Error echoing client's response.\n" ); closesocket( csock ); continue; } offset += written; len -= written; } } if ( len == -1 ) { // Read error occurred. fprintf( stderr, "Read error occurred on incoming client connection.\n" ); closesocket( csock ); continue; } closesocket( csock ); printf( "Connection closed. Processed %d bytes.\n", total_bytes ); } } int main() { SocketServices socket_services; // destructor closes sockets if ( ! socket_services.available() ) { fprintf( stderr, "Couldn't init sockets support, error %d.\n", socket_services.get_init_error() ); return socket_services.get_init_error(); } int ssock = get_server_socket(); if ( ssock < 0 ) { char const* err_str = 0; switch( ssock ) { case socket_error: err_str = "Couldn't acquire server socket"; break; case setsockopt_error: err_str = "Couldn't set server socket options"; break; case bind_error: err_str = "Couldn't bind server socket to port"; break; } if ( err_str ) fprintf( stderr, "%s\n", err_str ); return ssock; } // Start serving. serve_forever( ssock ); return 0; // keeps MSVC happy }