///////////////////////////////////////////////////////////////////// // socks.cpp - socket client and multi-threaded server // // prototype for project #2 // // ver 1.3 // // Language: Visual C++, ver 6.0, SP 5 // // Platform: Dell Dimension 8100, Win 2000 Pro, SP 2 // // Application: CSE691/791 - SWM&A Demonstration // // Author: Jim Fawcett, CST 2-187, Syracuse Univ // // (315) 443-3948, jfawcett@twcny.rr.com // ///////////////////////////////////////////////////////////////////// #include #include #include "socks.h" // must put here due to dependencies in Win32 defs #include "lockingPtr.h" #include "sysUtils.h" using namespace std; //----< handle WinSock errors >-------------------------------------- // // enum reportMethod { console, window, exception }; void reportError(reportMethod rm, const char *msg) { safeOut sout(cout); sysError sysErr; int errType = 0; if(msg) sout << "\n " << msg; switch(rm) { case console: errType = WSAGetLastError(); sout << "\n initialization error type: " << errType; sout << "\n " << sysErr.getLastMsg() << "\n\n"; break; case window : sout << "\n window reporting not implemented yet\n\n"; break; case exception: throw sysErr.getLastMsg(); default : sout << "\n invalid report method\n\n"; } } // //----< constructor >------------------------------------------------ sockClt::sockClt() { // load version 1.1 of winsock dll WORD wVersionRequested = MAKEWORD(1,1); // requesting version 1.1 WSAData wData; // startup data filled // by WSAStartup int err = WSAStartup(wVersionRequested, &wData); if(err == SOCKET_ERROR) { _good = false; reportError(console,"starting WinSock on client"); } // create TCP/IP socket object _client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); _good = true; } //----< attempt to connect to a server >----------------------------- bool sockClt::connectTo(address &addr, int port) { // connect to server on specified address and port // - note the IP address 127.0.0.1 refers to host machine _port = port; // server port struct hostent *serverHostent; serverHostent = gethostbyname("Lusitania"); SOCKADDR_IN tcpAddr; tcpAddr.sin_family = AF_INET; // TCP/IP tcpAddr.sin_addr.s_addr = inet_addr(addr.c_str()); // host machine tcpAddr.sin_port = htons(port); // server port int err = ::connect(_client, (sockaddr*)&tcpAddr, sizeof(tcpAddr)); if(err == SOCKET_ERROR) { reportError(console,"client can't connect"); return (_good = false); } return true; } // //----< attempt to connect to a server >----------------------------- bool sockClt::connectTo(const char *s, int port) { return connectTo(string(s), port); } //----< destructor >------------------------------------------------- sockClt::~sockClt() { closesocket(_client); WSACleanup(); } //----< insertion operator >----------------------------------------- sockClt& sockClt::operator<<(std::string &s) { static char *msg = "can't send all bytes in sockClt::operator<<(std::string &s)"; int bytesSent = send(s.c_str()); if(bytesSent != s.size()) reportError(console,msg); return *this; } //----< insertion operator >----------------------------------------- sockClt& sockClt::operator<<(const char *s) { return operator<<(string(s)); } //----< extraction operator >---------------------------------------- sockClt& sockClt::operator>>(std::string &s) { const int bufferSize = MAX_MSG_SIZE; char buffer[bufferSize]; int bytes = recv(_client,buffer,bufferSize,0); buffer[bytes] = '\0'; s = buffer; return *this; } // //----< send a block of bytes through socket connection >------------ // // attempts to guarantee that all bytes are sent // int sockClt::send(const char *block) { const char *msg = "send failed after 100 retries"; const int sendRetries = 100; int blockLen = strlen(block); int bytesSent; int bytesLeft = blockLen; int blockIndx = 0; int count = 0; while(bytesLeft > 0) { bytesSent = ::send(_client,&block[blockIndx],bytesLeft,0); if(bytesSent == SOCKET_ERROR) { Sleep(50); ++count; } if(count==sendRetries) { reportError(console,msg); _good = false; return bytesSent; } bytesLeft -= bytesSent; blockIndx += bytesSent; } return blockLen; } // //----< client will stop receiving >--------------------------------- void sockClt::shutDownRecv() { _good = false; shutdown(_client,SD_RECEIVE); } //----< client will stop sending >----------------------------------- void sockClt::shutDownSend() { _good = false; shutdown(_client,SD_SEND); } //----< client will stop sending and receiving >--------------------- void sockClt::shutDown() { _good = false; shutdown(_client,SD_BOTH); closesocket(_client); } //----< close socket connection >------------------------------------ void sockClt::close() { _good = false; closesocket(_client); } // //----< constructor >------------------------------------------------ sockSvr::sockSvr(int port) : _listeningPort(port) { // load version 1.1 of winsock dll WORD wVersionRequested = MAKEWORD(1,1); // requesting version 1.1 WSAData wData; // startup data filled // by WSAStartup int err = WSAStartup(wVersionRequested, &wData); if(err == SOCKET_ERROR) { reportError(console,"starting WinSock on server"); _good = false; } else _good = true; } //----< destructor >------------------------------------------------- sockSvr::~sockSvr() { _good = false; closesocket(_listener); WSACleanup(); } // //----< send a block of bytes through socket connection >------------ // // attempts to guarantee that all bytes are sent // int sockSvr::send(SOCKET sock, const char *block) { static char *msg = "send failed after 100 retries"; const int sendRetries = 100; int blockLen = strlen(block); int bytesSent; int bytesLeft = blockLen; int blockIndx = 0; int count = 0; while(bytesLeft > 0) { bytesSent = ::send(sock,&block[blockIndx],bytesLeft,0); if(bytesSent == SOCKET_ERROR) { Sleep(50); ++count; } if(count==sendRetries) { reportError(console,msg); // _good = false; return bytesSent; } bytesLeft -= bytesSent; blockIndx += bytesSent; } return blockLen; } // //----< start up server >-------------------------------------------- void sockSvr::start() { // create TCP/IP socket object to listen for connection requests _listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // bind listner socket to specific port and network interface SOCKADDR_IN tcpAddr; tcpAddr.sin_family = AF_INET; // use TCP/IP protocol tcpAddr.sin_port = htons(_listeningPort); // listening port tcpAddr.sin_addr.s_addr = INADDR_ANY; // listen over all NICs int err = bind(_listener, (SOCKADDR*)&tcpAddr, sizeof(tcpAddr)); safeOut sout(cout); if(err == SOCKET_ERROR) { reportError(console,"failed to bind"); _good = false; return; } // listen for incoming connection requests int backLog = 10; err = ::listen(_listener, backLog); if(err == SOCKET_ERROR) { reportError(console,"failed to start listening"); _good = false; return; } // sout << "\n waiting for incoming request"; int size = sizeof(tcpAddr); while(1) { // block until a connection is established SOCKET toClient = accept(_listener, (SOCKADDR*)&tcpAddr, &size); if(toClient == INVALID_SOCKET) { reportError(console,"could not create socket for new client"); break; } sout << locker << "\n\n established connection with: " << inet_ntoa(tcpAddr.sin_addr) << " : " << ntohs(tcpAddr.sin_port) << "\n" << unlocker; // got connection so spawn thread to read transferred data unsigned int threadID; HANDLE hThread = (HANDLE)_beginthreadex( NULL, 0, threadProc, (void*)&toClient, 0, &threadID ); CloseHandle(hThread); // listener doesn't need to communicate } // with thread so close thread handle } // //----< client connection thread processing >------------------------ // // With this ver 1.2 design, you must edit this function for any // given server application to get the processing you want. // Note carefully that this demonstration processing makes the // assumption: // // Every client message results in a return server message // so the client must listen after every sent message, // otherwise the client/server pair will deadlock. // // This design will be changed to be more generally useful in the // next release - presumably after you have turned in your second // project. // unsigned int __stdcall sockSvr::threadProc(void *pArgs) { SOCKET sock = *(SOCKET*)pArgs; char *msg = "server got your message"; safeOut sout(cout); string save; const int bufSize = MAX_MSG_SIZE; char buffer[bufSize]; int bytes; do { // recv reads all available bytes up to bufSize-1 // if no data is available it blocks until data // becomes available or the connection closes bytes = recv(sock,buffer,bufSize-1,0); if(bytes == SOCKET_ERROR) { sout << "\n\n client socket shutting down"; // reportError(console,"\n client socket shutting down"); break; } buffer[bytes] = '\0'; save = buffer; if(bytes > 0) { sout << locker << "\n message from client: " << save.c_str() << unlocker; if(!send(sock,msg)) { sysError err; sout << err.getLastMsg(); } else sout << locker << "\n reply sent to client: " << msg << unlocker; } } while(bytes != 0); sout << "\n server closing client thread\n"; shutdown(sock,SD_BOTH); closesocket(sock); return 0; } // //----< test stub >-------------------------------------------------- #ifdef TEST_SOCKS_CLIENT #include "thrdsWithProc.h" #include "sysUtils.h" /////////////////////////////////////////////////////////////// // define processing for client's receive thread class clientRcvrProc : public processing { public: clientRcvrProc(sockClt &client) : _client(client) { } virtual void run() { string rcvMsg; safeOut sout(cout); do { _client >> rcvMsg; if(_client.good()) sout << locker << "\n message from server: " << rcvMsg << unlocker; } while(_client.good()); sout << "\n client receiver thread shutting down"; } private: sockClt &_client; }; /////////////////////////////////////////////////////////////// // start running the client void main(int argc, char *argv[]) { safeOut sout(cout); sout << "\n running client\n"; sockClt client; if(!client.connectTo("127.0.0.1", 1024)) { reportError(console,"client could not connect"); return; } else sout << "\n connected to server"; clientRcvrProc proc(client); thread rcvThrd(proc); rcvThrd.start(); // string sndMsg/*, rcvMsg*/; int i; for(i=0; i<50; i++) { sndMsg = string("message #") += TtoStr(i).convert(); sout << locker << "\n sending to server: " << sndMsg << unlocker; client << sndMsg; Sleep(20); // give server time to grab this one string // -- remember that we are just sending strings, // not messages } sndMsg = "this is the last message from client"; sout << locker << "\n sending to server: " << sndMsg << unlocker; client << sndMsg; sout << "\n\n client shutting down"; client.shutDown(); rcvThrd.wait(); sout << "\n\n"; } #endif #ifdef TEST_SOCKS_SERVER void main() { cout << "\n running server\n"; try { sockSvr server(1024); server.start(); } catch(...) { cout << "\n can't create server - is there one already running?\n\n"; } } #endif