Advertisement
mvaganov

TCP/IP,WinSock2,Non-block,1Thread,Multi-Client,Echo Server

Jul 31st, 2013 (edited)
5,253
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 17.98 KB | None | 0 0
  1. // compile with: g++ socketexample.cpp -lwsock32
  2. #define _CRT_SECURE_NO_WARNINGS // ignore scanf warnings in visual studio
  3. #include <stdio.h>      // for printf
  4. #include <winsock2.h>   // the networking library.
  5. #include <Ws2tcpip.h>   // more tcp/ip functionality
  6. #pragma comment(lib, "ws2_32.lib") // links WinSock2 with MS Visual Studio
  7. #include <vector>       // for the server's client list and input buffer
  8. #include <conio.h>      // for kbhit() and getch()
  9.  
  10. int serverLogic(SOCKET mySocket, sockaddr* connectionAddress);
  11. int clientLogic(SOCKET mySocket, const sockaddr* connectionAddr);
  12. const int STATUS_READ = 0x1, STATUS_WRITE = 0x2, STATUS_EXCEPT = 0x4; // used by getStatus
  13. int getStatus(const SOCKET a_socket, int status);
  14. int endBrokerSocket(SOCKET socket);
  15. void finalWSACleanup();
  16.  
  17. int main()
  18. {
  19.     sockaddr_in connectionAddress;
  20.     unsigned short myPort = 1337;
  21.     int portInput;
  22.     printf("what port? "); scanf("%i", &portInput);
  23.     myPort = (short)portInput;
  24.     // initialize the socket's address
  25.     memset(&connectionAddress, 0, sizeof(sockaddr_in)); // initialize to zero
  26.     connectionAddress.sin_family = AF_INET;
  27.     // host-to-network-short: big-endian conversion of a 16 byte value
  28.     connectionAddress.sin_port = htons(myPort);
  29.     printf("changed endian-ness of port %d (%d %d) --htons-> %d (%d %d)\n", myPort, ((unsigned char*)(&myPort))[0], ((unsigned char*)(&myPort))[1],
  30.         connectionAddress.sin_port, ((unsigned char*)(&connectionAddress.sin_port))[0], ((unsigned char*)(&connectionAddress.sin_port))[1]);
  31.     // initialize the server socket
  32.     int result;
  33.     WSADATA wsaData; // gets populated w/ info explaining this sockets implementation
  34.     // load Winsock 2.0 DLL. initiates use of the Winsock DLL by a process
  35.     if ((result = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)
  36.     {
  37.         printf("WSAStartup() error %d\n", result);
  38.         return EXIT_FAILURE;
  39.     }
  40.     atexit(finalWSACleanup); // add callback to trigger when program ends. cleans up sockets
  41.     // create the main socket, either client or server
  42.     SOCKET mySocket = socket(
  43.         /*
  44.         AF_UNSPEC: 0     The address family is unspecified.
  45.         AF_INET: 2       The Internet Protocol version 4 (IPv4) address family.
  46.         AF_IPX: 6        The IPX/SPX address family. This address family is only supported if the NWLink IPX/SPX NetBIOS Compatible Transport protocol is installed. This address family is not supported on Windows Vista and later.
  47.         AF_APPLETALK: 17 The AppleTalk address family. This address family is only supported if the AppleTalk protocol is installed. This address family is not supported on Windows Vista and later.
  48.         AF_NETBIOS: 17   The NetBIOS address family. This address family is only supported if a Windows Sockets provider for NetBIOS is installed.
  49.         AF_INET6: 23     The Internet Protocol version 6 (IPv6) address family.
  50.         AF_IRDA: 26      The Infrared Data Association (IrDA) address family. This address family is only supported if the computer has an infrared port and driver installed.
  51.         AF_BTH: 32       The Bluetooth address family. This address family is supported on Windows XP with SP2 or later if the computer has a Bluetooth adapter and driver installed.
  52.         */
  53.         AF_INET,
  54.         /*
  55.         SOCK_STREAM: 1    A socket type that provides sequenced, reliable, two-way, connection-based byte streams with an OOB data transmission mechanism. This socket type uses the Transmission Control Protocol (TCP) for the Internet address family (AF_INET or AF_INET6).
  56.         SOCK_DGRAM: 2     A socket type that supports datagrams, which are connectionless, unreliable buffers of a fixed (typically small) maximum length. This socket type uses the User Datagram Protocol (UDP) for the Internet address family (AF_INET or AF_INET6).
  57.         SOCK_RAW: 3       A socket type that provides a raw socket that allows an application to manipulate the next upper-layer protocol header. To manipulate the IPv4 header, the IP_HDRINCL socket option must be set on the socket. To manipulate the IPv6 header, the IPV6_HDRINCL socket option must be set on the socket.
  58.         SOCK_RDM: 4       A socket type that provides a reliable message datagram. An example of this type is the Pragmatic General Multicast (PGM) multicast protocol implementation in Windows, often referred to as reliable multicast programming. This type is only supported if the Reliable Multicast Protocol is installed.
  59.         SOCK_SEQPACKET: 5 A socket type that provides a pseudo-stream packet based on datagrams.
  60.         */
  61.         SOCK_STREAM,
  62.         /*
  63.         BTHPROTO_RFCOMM: 3 The Bluetooth Radio Frequency Communications (Bluetooth RFCOMM) protocol. This is a possible value when the af parameter is AF_BTH and the type parameter is SOCK_STREAM. This protocol is supported on Windows XP with SP2 or later.
  64.         IPPROTO_TCP: 6     The Transmission Control Protocol (TCP). This is a possible value when the af parameter is AF_INET or AF_INET6 and the type parameter is SOCK_STREAM.
  65.         IPPROTO_UDP: 17    The User Datagram Protocol (UDP). This is a possible value when the af parameter is AF_INET or AF_INET6 and the type parameter is SOCK_DGRAM.
  66.         IPPROTO_RM: 113    The PGM protocol for reliable multicast. This is a possible value when the af parameter is AF_INET and the type parameter is SOCK_RDM. On the Windows SDK released for Windows Vista and later, this value is also called IPPROTO_PGM. This protocol is only supported if the Reliable Multicast Protocol is installed.
  67.         */
  68.         IPPROTO_TCP);
  69.     if (mySocket == INVALID_SOCKET)
  70.     {
  71.         printf("socket() error %d\n", WSAGetLastError());
  72.         return EXIT_FAILURE;
  73.     }
  74.     // 1 to set non-blocking, 0 to set re-usable
  75.     unsigned long argp = 1;
  76.     // for complete info on this method, check out http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx
  77.     result = setsockopt(mySocket,
  78.         SOL_SOCKET,
  79.         /*
  80.         SO_REUSEADDR: 4 Allows the socket to be bound to an address that is already in use. For more information, see bind. Not applicable on ATM sockets.
  81.         */
  82.         SO_REUSEADDR,
  83.         (char*)&argp, sizeof(argp));
  84.     if (result != 0)
  85.     {
  86.         printf("setsockopt() error %d\n", result);
  87.         return EXIT_FAILURE;
  88.     }
  89.     // 1 to set non-blocking, 0 to set blocking
  90.     argp = 1;
  91.     // attempt to setup the socket as non-blocking
  92.     if (ioctlsocket(mySocket,
  93.         /*
  94.         FIONBIO:    The *argp parameter is a pointer to an unsigned long value. Set *argp to a nonzero value if the nonblocking mode should be enabled, or zero if the nonblocking mode should be disabled. When a socket is created, it operates in blocking mode by default (nonblocking mode is disabled). This is consistent with BSD sockets.
  95.         FIONREAD:   Use to determine the amount of data pending in the network's input buffer that can be read from socket s. The argp parameter points to an unsigned long value in which ioctlsocket stores the result. FIONREAD returns the amount of data that can be read in a single call to the recv function, which may not be the same as the total amount of data queued on the socket. If s is message oriented (for example, type SOCK_DGRAM), FIONREAD still returns the amount of pending data in the network buffer, however, the amount that can actually be read in a single call to the recv function is limited to the data size written in the send or sendto function call.
  96.         SIOCATMARK: Use to determine if all out of band (OOB) data has been read. (See Windows Sockets 1.1 Blocking Routines and EINPROGRESS for a discussion on OOB data.) This applies only to a stream oriented socket (for example, type SOCK_STREAM) that has been configured for in-line reception of any OOB data (SO_OOBINLINE). On sockets with the SO_OOBINLINE socket option set, SIOCATMARK always returns TRUE and the OOB data is returned to the user as normal data.
  97.         */
  98.         FIONBIO,
  99.         &argp) == SOCKET_ERROR)
  100.     {
  101.         printf("ioctlsocket() error %d\n", WSAGetLastError());
  102.         return EXIT_FAILURE;
  103.     }
  104.     // make the decision about client or server socket here.
  105.     int userInput;
  106.     do {
  107.         printf("[c]lient or [s]erver?\n");
  108.         userInput = _getch();
  109.     } while (userInput != 'c' && userInput != 's');
  110.  
  111.     if (userInput == 's')
  112.     {
  113.         result = serverLogic(mySocket, (sockaddr*)&connectionAddress);
  114.     }
  115.     else
  116.     {
  117.         // connect to the server
  118.         const char* targetIP = "127.0.0.1"; // "::1"; // IPv6 localhost doesn't appear to work...
  119.         unsigned long raw_ip_nbo;// = inet_addr(targetIP); // inet_addr is an old method for IPv4
  120.         inet_pton(AF_INET, targetIP, &raw_ip_nbo); // IPv6 method of address acquisition
  121.         if (raw_ip_nbo == INADDR_NONE)
  122.         {
  123.             printf("inet_addr() error \"%s\"\n", targetIP);
  124.             return EXIT_FAILURE;
  125.         }
  126.         connectionAddress.sin_addr.s_addr = raw_ip_nbo;
  127.         result = clientLogic(mySocket, (const sockaddr*)&connectionAddress);
  128.     }
  129.     if (result == EXIT_FAILURE)
  130.     {
  131.         return EXIT_FAILURE;
  132.     }
  133.     if (mySocket != INVALID_SOCKET)
  134.     {
  135.         result = closesocket(mySocket); // server closes, it doesn't shut down
  136.         if (result != 0)
  137.         {
  138.             printf("closesocket() error %d\n", WSAGetLastError());
  139.             return EXIT_FAILURE;
  140.         }
  141.         mySocket = INVALID_SOCKET;
  142.     }
  143.     return EXIT_SUCCESS;
  144. }
  145.  
  146. int serverLogic(SOCKET mySocket, sockaddr* connectionAddress)
  147. {
  148.     // start listening on the server
  149.     int result = bind(mySocket, connectionAddress, sizeof(sockaddr_in));
  150.     if (result == SOCKET_ERROR)
  151.     {
  152.         printf("bind() error %d\n", WSAGetLastError());
  153.         return EXIT_FAILURE;
  154.     }
  155.     result = listen(mySocket, /* size of connection queue */10);
  156.     if (result == SOCKET_ERROR)
  157.     {
  158.         printf("listen() error %d\n", WSAGetLastError());
  159.         return EXIT_FAILURE;
  160.     }
  161.     // keep track of client connections
  162.     struct ClientSocket {
  163.         SOCKET socket;
  164.         sockaddr address;
  165.     };
  166.     // used to broker connection with clients
  167.     std::vector<ClientSocket> clientSockets;
  168.     // keep track of messages read from clients
  169.     std::vector<char> inputBuffer;
  170.     // server loop
  171.     int iterations = 0;
  172.     while ((result = getStatus(mySocket, STATUS_READ)) != SOCKET_ERROR)
  173.     {
  174.         if (_kbhit() && _getch() == 27) // escape key press
  175.         {
  176.             break; // end the server loop
  177.         }
  178.         if (result == 1)
  179.         {
  180.             printf("connecting client...");
  181.             int client_size = sizeof(sockaddr_in);
  182.             ClientSocket clientSocket;
  183.             clientSocket.socket = accept(mySocket,
  184.                 (struct sockaddr*) & clientSocket.address, &client_size);
  185.             if (clientSocket.socket == INVALID_SOCKET)
  186.             {
  187.                 printf("accept() error %d\n", WSAGetLastError());
  188.                 return EXIT_FAILURE;
  189.             }
  190.             clientSockets.push_back(clientSocket);
  191.             printf("\rconnected client %d\n", (int)clientSockets.size());
  192.         }
  193.         else
  194.         {
  195.             printf("server: %d\r", iterations++);
  196.             // read all the data from the clients
  197.             inputBuffer.clear();
  198.             for (unsigned int i = 0; i < clientSockets.size(); ++i)
  199.             {
  200.                 int status = getStatus(clientSockets[i].socket, STATUS_READ);
  201.                 if (status == 1)
  202.                 {
  203.                     if (clientSockets[i].socket == INVALID_SOCKET)
  204.                     {
  205.                         continue;
  206.                     }
  207.                     unsigned long howMuchInBuffer = 0;
  208.                     ioctlsocket(clientSockets[i].socket, FIONREAD,
  209.                         &howMuchInBuffer);
  210.                     if (howMuchInBuffer > 0)
  211.                     {
  212.                         unsigned long used = (unsigned long)inputBuffer.size();
  213.                         inputBuffer.resize((size_t)used + howMuchInBuffer, 0);
  214.                         result = recv(clientSockets[i].socket,
  215.                             (char*)(&inputBuffer[used]), howMuchInBuffer, 0);
  216.                         // NOTE: for multi-byte values sent across the network
  217.                         // ntohl or ntohs should be used to convert 4-byte and
  218.                         // 2-byte values respectively.
  219.                         if (result == SOCKET_ERROR)
  220.                         {
  221.                             printf("client %d recv() error %d\n", i,
  222.                                 WSAGetLastError());
  223.                             return EXIT_FAILURE;
  224.                         }
  225.                     }
  226.                 }
  227.                 else if (status == SOCKET_ERROR)
  228.                 {
  229.                     printf("\rclient %d read error\n", i);
  230.                     int result = endBrokerSocket(clientSockets[i].socket);
  231.                     if (result == EXIT_FAILURE)
  232.                     {
  233.                         return EXIT_FAILURE;
  234.                     }
  235.                     clientSockets[i].socket = INVALID_SOCKET;
  236.                 }
  237.             }
  238.             if (inputBuffer.size() > 0)
  239.             {
  240.                 // print data about to be sent
  241.                 printf("sending: \"");
  242.                 for (unsigned int i = 0; i < inputBuffer.size(); ++i)
  243.                 {
  244.                     printf("%c", inputBuffer[i]);
  245.                 }
  246.                 printf("\" %d bytes\n", (int)inputBuffer.size());
  247.                 // send all known data to all clients
  248.                 for (unsigned int i = 0; i < clientSockets.size(); ++i)
  249.                 {
  250.                     if (clientSockets[i].socket == INVALID_SOCKET)
  251.                     {
  252.                         continue;
  253.                     }
  254.                     // NOTE: for multi-byte values sent across the network
  255.                     // htonl or htons should be used to convert 4-byte and
  256.                     // 2-byte values respectively.
  257.                     result = send(clientSockets[i].socket,
  258.                         (const char*)&inputBuffer[0], (int)inputBuffer.size(), 0);
  259.                     if (result == SOCKET_ERROR) // if the socket failed
  260.                     {
  261.                         printf("\rclient %d send error %d\n", i,
  262.                             WSAGetLastError());
  263.                         int result = endBrokerSocket(clientSockets[i].socket);
  264.                         if (result == EXIT_FAILURE)
  265.                         {
  266.                             return EXIT_FAILURE;
  267.                         }
  268.                         clientSockets[i].socket = INVALID_SOCKET;
  269.                     }
  270.                 }
  271.             }
  272.             // removed released sockets from list, backwards to avoid skips
  273.             if (clientSockets.size() > 0)
  274.             {
  275.                 for (int i = (signed)clientSockets.size() - 1; i >= 0; --i)
  276.                 {
  277.                     if (clientSockets[i].socket == INVALID_SOCKET)
  278.                     {
  279.                         clientSockets.erase(clientSockets.begin() + i);
  280.                     }
  281.                 }
  282.             }
  283.         }
  284.     }
  285.     // release all client sockets
  286.     for (unsigned int i = 0; i < clientSockets.size(); ++i)
  287.     {
  288.         int result = endBrokerSocket(clientSockets[i].socket);
  289.         if (result == EXIT_FAILURE)
  290.         {
  291.             return EXIT_FAILURE;
  292.         }
  293.         clientSockets[i].socket = INVALID_SOCKET;
  294.     }
  295.     return EXIT_SUCCESS;
  296. }
  297.  
  298. int endBrokerSocket(SOCKET socket)
  299. {
  300.     int result = shutdown(socket, SD_BOTH);
  301.     if (result != 0)
  302.     {
  303.         printf("socket shutdown() error %d\n", WSAGetLastError());
  304.     }
  305.     result = closesocket(socket);
  306.     if (result != 0)
  307.     {
  308.         printf("socket closesocket() error %d\n", WSAGetLastError());
  309.         return EXIT_FAILURE;
  310.     }
  311.     return EXIT_SUCCESS;
  312. }
  313.  
  314. int clientLogic(SOCKET mySocket, const sockaddr* connectionAddress)
  315. {
  316.     int result, errorCode, connectionAttempts = 0;
  317.     bool connectionWaiting = false;
  318.     // initial connection loop
  319.     do
  320.     {
  321.         // allow user to quit gracefully with the escape key
  322.         if (_kbhit() && _getch() == 27)
  323.         {
  324.             return EXIT_FAILURE;
  325.         }
  326.         if (!connectionWaiting)
  327.         {
  328.             result = connect(mySocket, connectionAddress, sizeof(sockaddr_in));
  329.             errorCode = WSAGetLastError();
  330.         }
  331.         else
  332.         {
  333.             result = getStatus(mySocket, STATUS_WRITE);
  334.             if (result != 0)
  335.             {
  336.                 errorCode = result = WSAEISCONN;
  337.             }
  338.         }
  339.         switch (errorCode)
  340.         {
  341.         case 0:
  342.             connectionWaiting = true;
  343.             break;
  344.         case WSAEISCONN:
  345.             printf("connection succeeded!                    \n");
  346.             result = WSAEISCONN;
  347.             break;
  348.         case WSAEWOULDBLOCK:
  349.         case WSAEALREADY:
  350.             printf("waiting to connect... (press esc to quit)\r");
  351.             connectionWaiting = true;
  352.             break;
  353.         case WSAEINVAL:
  354.             printf("\ninvalid argument supplied\n");
  355.             return EXIT_FAILURE;
  356.         default:
  357.             printf("\nclient connect() error %d\n", errorCode);
  358.             return EXIT_FAILURE;
  359.         }
  360.     }
  361.     while (result == SOCKET_ERROR || result == 0);
  362.     // client loop
  363.     int iterations = 0;
  364.     bool sendit = false;
  365.     char userTextField[1024];
  366.     userTextField[0] = '\0';
  367.     int userTextFieldCursor = 0;
  368.     int userInput;
  369.     while (getStatus(mySocket, STATUS_READ) != SOCKET_ERROR)
  370.     {
  371.         // if user types, remember what's being typed, send with enter.
  372.         if (_kbhit())
  373.         {
  374.             userInput = _getch();
  375.             sendit = userInput == '\n' || userInput == '\r';
  376.             if (!sendit)
  377.             {
  378.                 putchar(userInput);
  379.                 userTextField[userTextFieldCursor++] = userInput;
  380.                 userTextField[userTextFieldCursor] = '\0';
  381.                 if (userTextFieldCursor >= sizeof(userTextField) - 1)
  382.                 {
  383.                     sendit = true;
  384.                 }
  385.             }
  386.             if (sendit)
  387.             {
  388.                 // NOTE: for multi-byte values sent across the network
  389.                 // htonl or htons should be used to convert 4-byte and
  390.                 // 2-byte values respectively.
  391.                 result = send(mySocket,
  392.                     (const char*)userTextField, userTextFieldCursor, 0);
  393.                 if (result == SOCKET_ERROR)
  394.                 {
  395.                     printf("client send() error %d\n", WSAGetLastError());
  396.                     return EXIT_FAILURE;
  397.                 }
  398.                 userTextFieldCursor = 0;
  399.                 userTextField[userTextFieldCursor] = '\0';
  400.             }
  401.         }
  402.         // receive data from the server, if there is any
  403.         if (getStatus(mySocket, STATUS_READ) == 1)
  404.         {
  405.             unsigned long howMuchInBuffer = 0;
  406.             unsigned long numBytesRead = 0;
  407.             printf("received: \"");
  408.             do
  409.             {
  410.                 ioctlsocket(mySocket, FIONREAD, &howMuchInBuffer);
  411.                 // 4 bytes at a time out of the socket, stored in userInput
  412.                 int result = recv(mySocket,
  413.                     (char*)(&userInput), sizeof(userInput), 0);
  414.                 // NOTE: for multi-byte values sent across the network
  415.                 // ntohl or ntohs should be used to convert 4-byte and
  416.                 // 2-byte values respectively.
  417.                 if (result == SOCKET_ERROR)
  418.                 {
  419.                     printf("client recv() error %d\n", WSAGetLastError());
  420.                     return EXIT_FAILURE;
  421.                 }
  422.                 for (int i = 0; i < result; ++i)
  423.                 {
  424.                     printf("%c", ((char*)(&userInput))[i]);
  425.                     numBytesRead++;
  426.                 }
  427.                 howMuchInBuffer -= result;
  428.             } while (howMuchInBuffer > 0);
  429.             printf("\" %d bytes%c", numBytesRead,
  430.                 ((numBytesRead != 0) ? '\n' : '\r'));
  431.         }
  432.         else
  433.         {
  434.             printf("client: \"%s\" %d\r", userTextField, iterations++);
  435.         }
  436.     }
  437.     return EXIT_SUCCESS;
  438. }
  439.  
  440. // status: 0x1 for read, 0x2 for write, 0x4 for exception
  441. int getStatus(const SOCKET a_socket, int status)
  442. {
  443.     // zero seconds, zero milliseconds. max time select call allowd to block
  444.     static timeval instantSpeedPlease = { 0,0 };
  445.     fd_set a = { 1, {a_socket} };
  446.     fd_set * read = ((status & 0x1) != 0) ? &a : NULL;
  447.     fd_set * write = ((status & 0x2) != 0) ? &a : NULL;
  448.     fd_set * except = ((status & 0x4) != 0) ? &a : NULL;
  449.     /*
  450.     select returns the number of ready socket handles in the fd_set structure, zero if the time limit expired, or SOCKET_ERROR if an error occurred. WSAGetLastError can be used to retrieve a specific error code.
  451.     */
  452.     int result = select(0, read, write, except, &instantSpeedPlease);
  453.     if (result == SOCKET_ERROR)
  454.     {
  455.         result = WSAGetLastError();
  456.     }
  457.     if (result < 0 || result > 3)
  458.     {
  459.         printf("select(read) error %d\n", result);
  460.         return SOCKET_ERROR;
  461.     }
  462.     return result;
  463. }
  464.  
  465. void finalWSACleanup() // callback used to clean up sockets
  466. {
  467.     int result = WSACleanup();
  468.     if (result != 0)
  469.     {
  470.         printf("WSACleanup() error %d\n", result);
  471.     }
  472. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement