///////////////////////////////////////////////////////////////////// // socks.cpp - socket client and multi-threaded server // // prototype for project #2 // // ver 1.2 // // 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; ///////////////////////////////////////////////////////////////////// // initialize static member // //CRITICAL_SECTION sockSvr::_cs; //----< 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) reportError(console,"starting WinSock on client"); // create TCP/IP socket object _client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); } //----< 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 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); return bytesSent; } bytesLeft -= bytesSent; blockIndx += bytesSent; } return blockLen; } //----< client will stop receiving >--------------------------------- void sockClt::shutDownRecv() { shutdown(_client,SD_RECEIVE); } //----< client will stop sending >----------------------------------- void sockClt::shutDownSend() { shutdown(_client,SD_SEND); } //----< client will stop sending and receiving >--------------------- void sockClt::shutDown() { shutdown(_client,SD_BOTH); closesocket(_client); } //----< close socket connection >------------------------------------ void sockClt::close() { 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"); // InitializeCriticalSection(&_cs); } //----< destructor >------------------------------------------------- sockSvr::~sockSvr() { // DeleteCriticalSection(&_cs); 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); 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"); return; } // listen for incoming connection requests int backLog = 10; err = ::listen(_listener, backLog); if(err == SOCKET_ERROR) { reportError(console,"failed to start listening"); 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 << "\n established connection with: " << inet_ntoa(tcpAddr.sin_addr) << " : " << ntohs(tcpAddr.sin_port) << "\n"; // 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) { reportError(console,"\n server error reading client socket"); break; } buffer[bytes] = '\0'; save = buffer; if(bytes > 0) { sout << "\n message from client: " << save.c_str(); if(!send(sock,msg)) { sysError err; sout << err.getLastMsg(); } else sout << "\n reply sent to client: " << msg; } } while(bytes != 0); sout << "\n server closing client thread"; closesocket(sock); return 0; } // //----< test stub >-------------------------------------------------- #ifdef TEST_SOCKS_CLIENT #include "threads.h" #include "sysUtils.h" void main(int argc, char *argv[]) { safeOut sout(cout); sout << "\n running client\n"; sockClt client; if(!client.connectTo("127.0.0.1", 1024)) return; else sout << "\n connected to server"; string sndMsg = "this is the first message from client"; sout << "\n sending to server: " << sndMsg; client << sndMsg; string rcvMsg; client >> rcvMsg; sout << "\n message from server: " << rcvMsg; sndMsg = "this is the last message from client"; sout << "\n sending to server: " << sndMsg; client << sndMsg; client >> rcvMsg; sout << "\n message from server: " << rcvMsg; sout << "\n client shutting down"; client.shutDown(); 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