// // server.c - Simple TCP/UDP server using Winsock 2.2 // // This is a part of the Microsoft Source Code Samples. // Copyright 1996 - 2000 Microsoft Corporation. // All rights reserved. // This source code is only intended as a supplement to // Microsoft Development Tools and/or WinHelp documentation. // See these sources for detailed information regarding the // Microsoft samples programs. // #define WIN32_LEAN_AND_MEAN #include #include #include // For IPv6 Tech Preview. #include #include #include // // This code assumes that at the transport level, the system only supports // one stream protocol (TCP) and one datagram protocol (UDP). Therefore, // specifying a socket type of SOCK_STREAM is equivalent to specifying TCP // and specifying a socket type of SOCK_DGRAM is equivalent to specifying UDP. // #define DEFAULT_FAMILY PF_UNSPEC // Accept either IPv4 or IPv6 #define DEFAULT_SOCKTYPE SOCK_STREAM // TCP #define DEFAULT_PORT "5001" // Arbitrary, albiet a historical test port #define BUFFER_SIZE 64 // Set very small for demonstration purposes void Usage(char *ProgName) { fprintf(stderr, "\nSimple socket sample server program.\n"); fprintf(stderr, "\n%s [-f family] [-t transport] [-p port] [-a address]\n\n", ProgName); fprintf(stderr, " family\tOne of PF_INET, PF_INET6 or PF_UNSPEC. (default %s)\n", (DEFAULT_FAMILY == PF_UNSPEC) ? "PF_UNSPEC" : ((DEFAULT_FAMILY == PF_INET) ? "PF_INET" : "PF_INET6")); fprintf(stderr, " transport\tEither TCP or UDP. (default: %s)\n", (DEFAULT_SOCKTYPE == SOCK_STREAM) ? "TCP" : "UDP"); fprintf(stderr, " port\t\tPort on which to bind. (default %s)\n", DEFAULT_PORT); fprintf(stderr, " address\tIP address on which to bind. (default: unspecified address)\n"); WSACleanup(); exit(1); } LPSTR DecodeError(int ErrorCode) { static char Message[1024]; // If this program was multi-threaded, we'd want to use // FORMAT_MESSAGE_ALLOCATE_BUFFER instead of a static buffer here. // (And of course, free the buffer when we were done with it) FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)Message, 1024, NULL); return Message; } int main(int argc, char **argv) { char Buffer[BUFFER_SIZE], Hostname[NI_MAXHOST]; int Family = DEFAULT_FAMILY; int SocketType = DEFAULT_SOCKTYPE; char *Port = DEFAULT_PORT; char *Address = NULL; int i, NumSocks, RetVal, FromLen, AmountRead; SOCKADDR_STORAGE From; WSADATA wsaData; ADDRINFO Hints, *AddrInfo, *AI; SOCKET ServSock[FD_SETSIZE]; fd_set SockSet; // Parse arguments if (argc > 1) { for(i = 1;i < argc; i++) { if ((argv[i][0] == '-') || (argv[i][0] == '/') && (argv[i][1] != 0) && (argv[i][2] == 0)) { switch(tolower(argv[i][1])) { case 'f': if (!argv[i+1]) Usage(argv[0]); if (!stricmp(argv[i+1], "PF_INET")) Family = PF_INET; else if (!stricmp(argv[i+1], "PF_INET6")) Family = PF_INET6; else if (!stricmp(argv[i+1], "PF_UNSPEC")) Family = PF_UNSPEC; else Usage(argv[0]); i++; break; case 't': if (!argv[i+1]) Usage(argv[0]); if (!stricmp(argv[i+1], "TCP")) SocketType = SOCK_STREAM; else if (!stricmp(argv[i+1], "UDP")) SocketType = SOCK_DGRAM; else Usage(argv[0]); i++; break; case 'a': if (argv[i+1]) { if (argv[i+1][0] != '-') { Address = argv[++i]; break; } } Usage(argv[0]); break; case 'p': if (argv[i+1]) { if (argv[i+1][0] != '-') { Port = argv[++i]; break; } } Usage(argv[0]); break; default: Usage(argv[0]); break; } } else Usage(argv[0]); } } // Ask for Winsock version 2.2. if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) { fprintf(stderr, "WSAStartup failed with error %d: %s\n", RetVal, DecodeError(RetVal)); WSACleanup(); return -1; } if (Port == NULL) { Usage(argv[0]); } // // By setting the AI_PASSIVE flag in the hints to getaddrinfo, we're // indicating that we intend to use the resulting address(es) to bind // to a socket(s) for accepting incoming connections. This means that // when the Address parameter is NULL, getaddrinfo will return one // entry per allowed protocol family containing the unspecified address // for that family. // memset(&Hints, 0, sizeof(Hints)); Hints.ai_family = Family; Hints.ai_socktype = SocketType; Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; RetVal = getaddrinfo(Address, Port, &Hints, &AddrInfo); if (RetVal != 0) { fprintf(stderr, "getaddrinfo failed with error %d: %s\n", RetVal, gai_strerror(RetVal)); WSACleanup(); return -1; } // // For each address getaddrinfo returned, we create a new socket, // bind that address to it, and create a queue to listen on. // for (i = 0, AI = AddrInfo; AI != NULL; AI = AI->ai_next, i++) { // Highly unlikely, but check anyway. if (i == FD_SETSIZE) { printf("getaddrinfo returned more addresses than we could use.\n"); break; } // This example only supports PF_INET and PF_INET6. if ((AI->ai_family != PF_INET) && (AI->ai_family != PF_INET6)) continue; // Open a socket with the correct address family for this address. ServSock[i] = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol); if (ServSock[i] == INVALID_SOCKET){ fprintf(stderr, "socket() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); continue; } // // bind() associates a local address and port combination // with the socket just created. This is most useful when // the application is a server that has a well-known port // that clients know about in advance. // if (bind(ServSock[i], AI->ai_addr, AI->ai_addrlen) == SOCKET_ERROR) { fprintf(stderr,"bind() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); continue; } // // So far, everything we did was applicable to TCP as well as UDP. // However, there are certain fundamental differences between stream // protocols such as TCP and datagram protocols such as UDP. // // Only connection orientated sockets, for example those of type // SOCK_STREAM, can listen() for incoming connections. // if (SocketType == SOCK_STREAM) { if (listen(ServSock[i], 5) == SOCKET_ERROR) { fprintf(stderr, "listen() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); continue; } } printf("'Listening' on port %s, protocol %s, protocol family %s\n", Port, (SocketType == SOCK_STREAM) ? "TCP" : "UDP", (AI->ai_family == PF_INET) ? "PF_INET" : "PF_INET6"); } freeaddrinfo(AddrInfo); if (i == 0) { fprintf(stderr, "Fatal error: unable to serve on any address.\n"); WSACleanup(); return -1; } NumSocks = i; // // We now put the server into an eternal loop, // serving requests as they arrive. // FD_ZERO(&SockSet); while(1) { FromLen = sizeof(From); // // For connection orientated protocols, we will handle the // packets comprising a connection collectively. For datagram // protocols, we have to handle each datagram individually. // // // Check to see if we have any sockets remaining to be served // from previous time through this loop. If not, call select() // to wait for a connection request or a datagram to arrive. // for (i = 0; i < NumSocks; i++){ if (FD_ISSET(ServSock[i], &SockSet)) break; } if (i == NumSocks) { for (i = 0; i < NumSocks; i++) FD_SET(ServSock[i], &SockSet); if (select(NumSocks, &SockSet, 0, 0, 0) == SOCKET_ERROR) { fprintf(stderr, "select() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); WSACleanup(); return -1; } } for (i = 0; i < NumSocks; i++){ if (FD_ISSET(ServSock[i], &SockSet)) { FD_CLR(ServSock[i], &SockSet); break; } } if (SocketType == SOCK_STREAM) { SOCKET ConnSock; // // Since this socket was returned by the select(), we know we // have a connection waiting and that this accept() won't block. // ConnSock = accept(ServSock[i], (LPSOCKADDR)&From, &FromLen); if (ConnSock == INVALID_SOCKET) { fprintf(stderr, "accept() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); WSACleanup(); return -1; } if (getnameinfo((LPSOCKADDR)&From, FromLen, Hostname, sizeof(Hostname), NULL, 0, NI_NUMERICHOST) != 0) strcpy(Hostname, ""); printf("\nAccepted connection from %s\n", Hostname); // // This sample server only handles connections sequentially. // To handle multiple connections simultaneously, a server // would likely want to launch another thread or process at this // point to handle each individual connection. Alternatively, // it could keep a socket per connection and use select() // on the fd_set to determine which to read from next. // // Here we just loop until this connection terminates. // while (1) { // // We now read in data from the client. Because TCP // does NOT maintain message boundaries, we may recv() // the client's data grouped differently than it was // sent. Since all this server does is echo the data it // receives back to the client, we don't need to concern // ourselves about message boundaries. But it does mean // that the message data we print for a particular recv() // below may contain more or less data than was contained // in a particular client send(). // AmountRead = recv(ConnSock, Buffer, sizeof(Buffer), 0); if (AmountRead == SOCKET_ERROR) { fprintf(stderr, "recv() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); closesocket(ConnSock); break; } if (AmountRead == 0) { printf("Client closed connection\n"); closesocket(ConnSock); break; } printf("Received %d bytes from client: [%.*s]\n", AmountRead, AmountRead, Buffer); printf("Echoing same data back to client\n"); RetVal = send(ConnSock, Buffer, AmountRead, 0); if (RetVal == SOCKET_ERROR) { fprintf(stderr, "send() failed: error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); closesocket(ConnSock); break; } } } else { // // Since UDP maintains message boundaries, the amount of data // we get from a recvfrom() should match exactly the amount of // data the client sent in the corresponding sendto(). // AmountRead = recvfrom(ServSock[i], Buffer, sizeof(Buffer), 0, (LPSOCKADDR)&From, &FromLen); if (AmountRead == SOCKET_ERROR) { fprintf(stderr, "recvfrom() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); closesocket(ServSock[i]); break; } if (AmountRead == 0) { // This should never happen on an unconnected socket, but... printf("recvfrom() returned zero, aborting\n"); closesocket(ServSock[i]); break; } RetVal = getnameinfo((LPSOCKADDR)&From, FromLen, Hostname, sizeof(Hostname), NULL, 0, NI_NUMERICHOST); if (RetVal != 0) { fprintf(stderr, "getnameinfo() failed with error %d: %s\n", RetVal, DecodeError(RetVal)); strcpy(Hostname, ""); } printf("Received a %d byte datagram from %s: [%.*s]\n", AmountRead, Hostname, AmountRead, Buffer); printf("Echoing same data back to client\n"); RetVal = sendto(ServSock[i], Buffer, AmountRead, 0, (LPSOCKADDR)&From, FromLen); if (RetVal == SOCKET_ERROR) { fprintf(stderr, "send() failed with error %d: %s\n", WSAGetLastError(), DecodeError(WSAGetLastError())); } } } return 0; }