310 likes | 502 Views
Winsock Programming. Blocking and Asynchronous Sockets for Windows. History. MS-DOS/Windows not designed for networking Early Microsoft operating systems ignored the TCP/IP stack “Visionary” Bill Gates supports NETBIOS over TCP/IP Several packages offered by 3 rd parties
E N D
Winsock Programming Blocking and Asynchronous Sockets for Windows
History • MS-DOS/Windows not designed for networking • Early Microsoft operating systems ignored the TCP/IP stack • “Visionary” Bill Gates supports NETBIOS over TCP/IP • Several packages offered by 3rd parties • “Trumpet Winsock” • None work particularly well
What is Winsock? • API, SPI and ABI • API for application developers • SPI for network software vendors • ABI to ensure consistent interoperability between applications and various implementations of Winsock • Less important now that Microsoft has released a quality Winsock • No significant alternatives
The API • Based on BSD sockets API • First of many Microsoft “hacks” • Complications due to differences between Unix and Windows • errno (Unix) vs. WSAGetLastError() • BSD sockets based on C and Unix • fork() • File descriptors
Example: The Unix Way while (1) { newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) error("ERROR on accept"); pid = fork(); if (pid < 0) error("ERROR on fork"); if (pid == 0) { close(sockfd); dostuff(newsockfd); exit(0); } else close(newsockfd); }
Steps to a Listen Socket • Initialize Winsock • Fill out SOCKADDR_IN (define the socket) • Create the socket • bind() the socket • Make the socket listen() • Wait on accept() • Handle clients via socket returned by accept()
Example: Listening (simplified) // INITIALIZE sockVersion = MAKEWORD(1, 1); // Winsock version 1.1 WSAStartup(sockVersion, &wsaData); // CREATE SOCKET SOCKET listeningSocket; listeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // DEFINE SOCKET SOCKADDR_IN serverInfo; serverInfo.sin_family = AF_INET; serverInfo.sin_addr.s_addr = INADDR_ANY; serverInfo.sin_port = htons(8888); // BIND bind(listeningSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)); // LISTEN listen(listeningSocket, 10); // ACCEPT SOCKET theClient; theClient = accept(listeningSocket, NULL, NULL);
Steps to Connecting • Initialize Winsock • Fill out HOSTENT • Create socket • Fill out SOCKADDR_IN • Connect • Send/recv
Example: Connecting (simplified) // INITIALIZE sockVersion = MAKEWORD(1, 1); WSAStartup(sockVersion, &wsaData); // FILL OUT HOSTENT LPHOSTENT hostEntry = gethostbyname("www.hal-pc.org"); // CREATE SOCKET SOCKET theSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // FILL OUT SOCKADDR_IN SOCKADDR_IN serverInfo; serverInfo.sin_family = AF_INET; serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list); serverInfo.sin_port = htons(80); // CONNECT connect(theSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
Sending and Receiving • int send(SOCKET s, char * buf, int len, int flags); • Returns number of bytes sent or SOCKET_ERROR • int recv(SOCKET s, char * buf, int len, int flags); • Returns number of bytes received or SOCKET_ERROR • Send and recv must be done in loops to ensure all data is sent/received
Problem: blocking • Many of these functions “block” • accept() • connect() • send() • recv() • One solution: poll sockets using select() • Another solution: threads • Coolest solution: asynchronous sockets
Why Asynchronous? • Windows handles polling for you • Familiar Windows message paradigm • Easy to read code
Why NOT Asynchronous? • Non-portable • Microsoft is evil • Use them anyways, they are cool
Windows Messages • Message queue • Message pump • “first chance” message handling • Message Handler • Callback function
Sample Message Pump HRESULT hRet; UINT Msg; while( (hRet = GetMessage( &Msg, NULL, 0, 0 )) != 0) { if (hRet == -1) { PostQuitMessage(1); } else { TranslateMessage(&Msg); DispatchMessage(&Msg); } }
Sample Message Handler LRESULT CALLBACK WinProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY: PostQuitMessage(WM_QUIT); break; default: return FALSE; } return TRUE; }
How Does This Apply? • Windows sends messages when sockets are waiting • No need to poll • No need for many threads • Create sockets and go about other tasks • Handle sockets just like any other Windows control
Making a socket asynchronous int PASCAL WSAAsyncSelect( SOCKET s, HWND hwnd, unsigned int Msg, long event ); Simple call to WSAAsyncSelect:
Message and Events • Five events: • FD_READ • FD_WRITE • FD_CONNECT • FD_ACCEPT • FD_CLOSE • OR these together to tell Windows what notifications you need • User defined messages • WM_USER + n
Handling the message • wParam: • socket instance • lParam: • HIWORD: • 0 (success) • Error code • LOWORD: • FD_READ, FD_WRITE, etc.
Example: Handling FD_ACCEPT SOCKET s; switch (msg) { case WM_USER + 5: switch(WSAGETSELECTEVENT(lParam) { case FD_ACCEPT: s = accept(wParam, NULL, NULL); break; } break; }
Summary • Why this is important: • Current Winsock responsible for increase in quantity and popularity of Internet applications • Almost all applications use the Internet somehow, or could be improved by using the Internet
Example: Version Checking DWORD WINAPI CGlowdotChatServer::DoVersionCheckThreadProc(LPVOID pvoid) { struct sockaddr_in sa; struct hostent *hp; hp = gethostbyname(“www.stromcode.com”); memset(&sa,0,sizeof(sa)); // set address and port memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length); sa.sin_family = hp->h_addrtype; sa.sin_port = htons((u_short)VERSION_CHECK_PORT); // create socket SOCKET s = socket(hp->h_addrtype, SOCK_STREAM, 0); std::string request ("GET /glowdotchat/server/checkversion.php?getcurrentversion=yes HTTP/1.1\nUser-Agent:Mozilla\nHost:www.stromcode.com\n\n"); send(s, request.c_str(), request.length(),0); char buf[1000]; std::string response(""); recv(s, buf, 1000, 0); response += std::string(buf); std::string::size_type pos1 = response.find(std::string("<current-version>")); std::string::size_type pos2 = response.find(std::string("</current-version>")); response = response.substr(pos1 + 17, pos2 - pos1 - 17); if (response == std::string(VERSION)) { // server up to date } else { // new version available } return 0; }