daily pastebin goal
48%
SHARE
TWEET

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

mvaganov Jul 31st, 2013 (edited) 304 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <stdio.h>              // for printf
  2. #include <winsock2.h>   // the networking library
  3. #pragma comment(lib, "ws2_32.lib")      // links WinSock2 with MS Visual Studio
  4. #include <vector>       // for the server's client list and input buffer
  5. #include <conio.h>      // for kbhit() and getch()
  6.  
  7. int serverLogic(SOCKET mySocket, sockaddr * connectionAddress);
  8. int clientLogic(SOCKET mySocket, const sockaddr* connectionAddr);
  9. int getReadStatus(const SOCKET a_socket);
  10. int endBrokerSocket(SOCKET socket);
  11. void finalWSACleanup();
  12.  
  13. int main()
  14. {
  15.         sockaddr_in connectionAddress;
  16.         unsigned short myPort = 1337;
  17.         // initialize the socket's address
  18.         memset(&connectionAddress, 0, sizeof(sockaddr_in)); // initialize to zero
  19.         connectionAddress.sin_family = AF_INET;
  20.         // host-to-network-short: big-endian conversion of a 16 byte value
  21.         connectionAddress.sin_port = htons(myPort);
  22.         // initialize the server socket
  23.         int result;
  24.         WSADATA wsaData; // gets populated w/ info explaining this sockets implementation
  25.         // load Winsock 2.0 DLL. initiates use of the Winsock DLL by a process
  26.         if ((result = WSAStartup(MAKEWORD(1, 1),&wsaData)) != 0)
  27.         {
  28.                 printf("WSAStartup() error %d\n", result);
  29.                 return EXIT_FAILURE;
  30.         }
  31.         atexit(finalWSACleanup); // add callback to trigger when program ends. cleans up sockets
  32.         // create the main socket, either client or server
  33.         SOCKET mySocket = socket(
  34. /*
  35. AF_UNSPEC: 0     The address family is unspecified.
  36. AF_INET: 2       The Internet Protocol version 4 (IPv4) address family.
  37. 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.
  38. 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.
  39. AF_NETBIOS: 17   The NetBIOS address family. This address family is only supported if a Windows Sockets provider for NetBIOS is installed.
  40. AF_INET6: 23     The Internet Protocol version 6 (IPv6) address family.
  41. 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.
  42. 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.
  43. */
  44.                 AF_INET,
  45. /*
  46. 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).
  47. 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).
  48. 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.
  49. 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.
  50. SOCK_SEQPACKET: 5 A socket type that provides a pseudo-stream packet based on datagrams.
  51. */
  52.                 SOCK_STREAM,
  53. /*
  54. 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.
  55. 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.
  56. 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.
  57. 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.
  58. */
  59.                 IPPROTO_TCP);
  60.         if (mySocket == INVALID_SOCKET)
  61.         {
  62.                 printf("socket() error %d\n", WSAGetLastError());
  63.                 return EXIT_FAILURE;
  64.         }
  65.         // 1 to set non-blocking, 0 to set re-usable
  66.         unsigned long argp = 1;
  67.         // for complete info on this method, check out http://msdn.microsoft.com/en-us/library/ms740476(VS.85).aspx
  68.         result = setsockopt(mySocket,
  69.                 SOL_SOCKET,
  70. /*
  71. 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.
  72. */
  73.                 SO_REUSEADDR,
  74.                 (char *)&argp, sizeof(argp));
  75.         if (result != 0)
  76.         {
  77.                 printf("setsockopt() error %d\n", result);
  78.                 return EXIT_FAILURE;
  79.         }
  80.         // 1 to set non-blocking, 0 to set blocking
  81.         argp = 1;
  82.         // attempt to setup the socket as non-blocking
  83.         if(ioctlsocket(mySocket,
  84. /*
  85. 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.
  86. 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.
  87. 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.
  88. */
  89.                         FIONBIO,
  90.                         &argp) == SOCKET_ERROR)
  91.         {
  92.                 printf("ioctlsocket() error %d\n", WSAGetLastError());
  93.                 return EXIT_FAILURE;
  94.         }
  95.         // make the decision about client or server socket here.
  96.         int userInput;
  97.         do{
  98.                 printf("[c]lient or [s]erver?\n");
  99.                 userInput = _getch();
  100.         }while(userInput != 'c' && userInput != 's');
  101.  
  102.         if(userInput == 's')
  103.         {
  104.                 result = serverLogic(mySocket, (sockaddr*)&connectionAddress);
  105.         }
  106.         else
  107.         {
  108.                 // connect to the server
  109.                 char * targetIP = "127.0.0.1";
  110.                 unsigned long raw_ip_nbo = inet_addr(targetIP);
  111.                 if (raw_ip_nbo == INADDR_NONE)
  112.                 {
  113.                         printf("inet_addr() error \"%s\"\n", targetIP);
  114.                         return EXIT_FAILURE;
  115.                 }
  116.                 connectionAddress.sin_addr.s_addr = raw_ip_nbo;
  117.                 result = clientLogic(mySocket, (const sockaddr*)&connectionAddress);
  118.         }
  119.         if(result == EXIT_FAILURE)
  120.         {
  121.                 return EXIT_FAILURE;
  122.         }
  123.         if(mySocket != INVALID_SOCKET)
  124.         {
  125.                 result = closesocket(mySocket); // server closes, it doesn't shut down
  126.                 if (result != 0)
  127.                 {
  128.                         printf("closesocket() error %d\n", WSAGetLastError());
  129.                         return EXIT_FAILURE;
  130.                 }
  131.                 mySocket = INVALID_SOCKET;
  132.         }
  133.         return EXIT_SUCCESS;
  134. }
  135.  
  136. int serverLogic(SOCKET mySocket, sockaddr * connectionAddress)
  137. {
  138.         // start listening on the server
  139.         int result = bind(mySocket, connectionAddress, sizeof(sockaddr_in));
  140.         if (result == SOCKET_ERROR)
  141.         {
  142.                 printf("bind() error %d\n", WSAGetLastError());
  143.                 return EXIT_FAILURE;
  144.         }
  145.         result = listen(mySocket, /* size of connection queue */10);
  146.         if (result == SOCKET_ERROR)
  147.         {
  148.                 printf("listen() error %d\n", WSAGetLastError());
  149.                 return EXIT_FAILURE;
  150.         }
  151.         // keep track of client connections
  152.         struct ClientSocket {
  153.                 SOCKET socket;
  154.                 sockaddr address;
  155.         };
  156.         // used to broker connection with clients
  157.         std::vector<ClientSocket> clientSockets;
  158.         // keep track of messages read from clients
  159.         std::vector<char> inputBuffer;
  160.         // server loop
  161.         int iterations = 0;
  162.         while((result = getReadStatus(mySocket)) != SOCKET_ERROR)
  163.         {
  164.                 if(_kbhit() && _getch() == 27)  // escape key press
  165.                 {
  166.                         break; // end the server loop
  167.                 }
  168.                 if(result == 1)
  169.                 {
  170.                         printf("connecting client...");
  171.                         int client_size = sizeof(sockaddr_in);
  172.                         ClientSocket clientSocket;
  173.                         clientSocket.socket = accept(mySocket,
  174.                                 (struct sockaddr*)&clientSocket.address, &client_size);
  175.                         if(clientSocket.socket == INVALID_SOCKET)
  176.                         {
  177.                                 printf("accept() error %d\n", WSAGetLastError());
  178.                                 return EXIT_FAILURE;
  179.                         }
  180.                         clientSockets.push_back(clientSocket);
  181.                         printf("\rconnected client %d\n", clientSockets.size());
  182.                 }
  183.                 else
  184.                 {
  185.                         printf("server: %d\r", iterations++);
  186.                         // read all the data from the clients
  187.                         inputBuffer.clear();
  188.                         for(unsigned int i = 0; i < clientSockets.size(); ++i)
  189.                         {
  190.                                 int status = getReadStatus(clientSockets[i].socket);
  191.                                 if(status == 1)
  192.                                 {
  193.                                         if(clientSockets[i].socket == INVALID_SOCKET)
  194.                                         {
  195.                                                 continue;
  196.                                         }
  197.                                         unsigned long howMuchInBuffer = 0;
  198.                                         ioctlsocket(clientSockets[i].socket, FIONREAD,
  199.                                                 &howMuchInBuffer);
  200.                                         if(howMuchInBuffer > 0)
  201.                                         {
  202.                                                 unsigned long used = inputBuffer.size();
  203.                                                 inputBuffer.resize(used + howMuchInBuffer, 0);
  204.                                                 result = recv(clientSockets[i].socket,
  205.                                                         (char*)(&inputBuffer[used]),howMuchInBuffer,0);
  206.                                                 // NOTE: for multi-byte values sent across the network
  207.                                                 // ntohl or ntohs should be used to convert 4-byte and
  208.                                                 // 2-byte values respectively.
  209.                                                 if(result == SOCKET_ERROR)
  210.                                                 {
  211.                                                         printf("client %d recv() error %d\n", i,
  212.                                                                 WSAGetLastError());
  213.                                                         return EXIT_FAILURE;
  214.                                                 }
  215.                                         }
  216.                                 }
  217.                                 else if(status == SOCKET_ERROR)
  218.                                 {
  219.                                         printf("\rclient %d read error\n", i);
  220.                                         int result = endBrokerSocket(clientSockets[i].socket);
  221.                                         if(result == EXIT_FAILURE)
  222.                                         {
  223.                                                 return EXIT_FAILURE;
  224.                                         }
  225.                                         clientSockets[i].socket = INVALID_SOCKET;
  226.                                 }
  227.                         }
  228.                         if(inputBuffer.size() > 0)
  229.                         {
  230.                                 // print data about to be sent
  231.                                 printf("sending: \"");
  232.                                 for(unsigned int i = 0; i < inputBuffer.size(); ++i)
  233.                                 {
  234.                                         printf("%c", inputBuffer[i]);
  235.                                 }
  236.                                 printf("\" %d bytes\n", inputBuffer.size());
  237.                                 // send all known data to all clients
  238.                                 for(unsigned int i = 0; i < clientSockets.size(); ++i)
  239.                                 {
  240.                                         if(clientSockets[i].socket == INVALID_SOCKET)
  241.                                         {
  242.                                                 continue;
  243.                                         }
  244.                                         // NOTE: for multi-byte values sent across the network
  245.                                         // htonl or htons should be used to convert 4-byte and
  246.                                         // 2-byte values respectively.
  247.                                         result = send(clientSockets[i].socket,
  248.                                                 (const char*)&inputBuffer[0],inputBuffer.size(),0);
  249.                                         if(result == SOCKET_ERROR) // if the socket failed
  250.                                         {
  251.                                                 printf("\rclient %d send error %d\n", i,
  252.                                                         WSAGetLastError());
  253.                                                 int result = endBrokerSocket(clientSockets[i].socket);
  254.                                                 if(result == EXIT_FAILURE)
  255.                                                 {
  256.                                                         return EXIT_FAILURE;
  257.                                                 }
  258.                                                 clientSockets[i].socket = INVALID_SOCKET;
  259.                                         }
  260.                                 }
  261.                         }
  262.                         // removed released sockets from list, backwards to avoid skips
  263.                         if(clientSockets.size() > 0)
  264.                         {
  265.                                 for(int i = (signed)clientSockets.size()-1; i >= 0; --i)
  266.                                 {
  267.                                         if(clientSockets[i].socket == INVALID_SOCKET)
  268.                                         {
  269.                                                 clientSockets.erase(clientSockets.begin()+i);
  270.                                         }
  271.                                 }
  272.                         }
  273.                 }
  274.         }
  275.         // release all client sockets
  276.         for(unsigned int i = 0; i < clientSockets.size(); ++i)
  277.         {
  278.                 int result = endBrokerSocket(clientSockets[i].socket);
  279.                 if(result == EXIT_FAILURE)
  280.                 {
  281.                         return EXIT_FAILURE;
  282.                 }
  283.                 clientSockets[i].socket = INVALID_SOCKET;
  284.         }
  285.         return EXIT_SUCCESS;
  286. }
  287.  
  288. int endBrokerSocket(SOCKET socket)
  289. {
  290.         int result = shutdown(socket, SD_BOTH);
  291.         if (result != 0)
  292.         {
  293.                 printf("socket shutdown() error %d\n", WSAGetLastError());
  294.         }
  295.         result = closesocket(socket);
  296.         if (result != 0)
  297.         {
  298.                 printf("socket closesocket() error %d\n", WSAGetLastError());
  299.                 return EXIT_FAILURE;
  300.         }
  301.         return EXIT_SUCCESS;
  302. }
  303.  
  304. int clientLogic(SOCKET mySocket, const sockaddr* connectionAddress)
  305. {
  306.         int result = connect(mySocket, connectionAddress, sizeof(sockaddr_in));
  307.         if(result == SOCKET_ERROR)
  308.         {
  309.                 int errorCode = WSAGetLastError();
  310.                 switch(errorCode)
  311.                 {
  312.                 case WSAEISCONN:        printf("already connected!\n"); break;
  313.                 case WSAEWOULDBLOCK:
  314.                 case WSAEALREADY:
  315.                         break;
  316.                 default:
  317.                         printf("client connect() error %d\n", errorCode);
  318.                         return EXIT_FAILURE;
  319.                 }
  320.         }
  321.         // client loop
  322.         int iterations = 0;
  323.         bool sendit = false;
  324.         char userTextField[1024];
  325.         userTextField[0] = '\0';
  326.         int userTextFieldCursor = 0;
  327.         int userInput;
  328.         while(getReadStatus(mySocket) != SOCKET_ERROR)
  329.         {
  330.                 // if user types, remember what's being typed, send with enter.
  331.                 if(_kbhit())
  332.                 {
  333.                         userInput = _getch();
  334.                         sendit = userInput == '\n' || userInput == '\r';
  335.                         if(!sendit)
  336.                         {
  337.                                 putchar(userInput);
  338.                                 userTextField[userTextFieldCursor++] = userInput;
  339.                                 userTextField[userTextFieldCursor] = '\0';
  340.                                 if(userTextFieldCursor >= sizeof(userTextField)-1)
  341.                                 {
  342.                                         sendit = true;
  343.                                 }
  344.                         }
  345.                         if(sendit)
  346.                         {
  347.                                 // NOTE: for multi-byte values sent across the network
  348.                                 // htonl or htons should be used to convert 4-byte and
  349.                                 // 2-byte values respectively.
  350.                                 result = send(mySocket,
  351.                                         (const char *)userTextField, userTextFieldCursor, 0);
  352.                                 if(result == SOCKET_ERROR)
  353.                                 {
  354.                                         printf("client send() error %d\n", WSAGetLastError());
  355.                                         return EXIT_FAILURE;
  356.                                 }
  357.                                 userTextFieldCursor = 0;
  358.                                 userTextField[userTextFieldCursor] = '\0';
  359.                         }
  360.                 }
  361.                 // receive data from the server, if there is any
  362.                 if(getReadStatus(mySocket) == 1)
  363.                 {
  364.                         unsigned long howMuchInBuffer = 0;
  365.                         unsigned long numBytesRead = 0;
  366.                         printf("received: \"");
  367.                         do
  368.                         {
  369.                                 ioctlsocket(mySocket, FIONREAD, &howMuchInBuffer);
  370.                                 // 4 bytes at a time out of the socket, stored in userInput
  371.                                 int result = recv(mySocket,
  372.                                         (char*)(&userInput), sizeof(userInput), 0);
  373.                                 // NOTE: for multi-byte values sent across the network
  374.                                 // ntohl or ntohs should be used to convert 4-byte and
  375.                                 // 2-byte values respectively.
  376.                                 if(result == SOCKET_ERROR)
  377.                                 {
  378.                                         printf("client recv() error %d\n", WSAGetLastError());
  379.                                         return EXIT_FAILURE;
  380.                                 }
  381.                                 for(int i = 0; i < result; ++i)
  382.                                 {
  383.                                         printf("%c",  ((char*)(&userInput))[i]);
  384.                                         numBytesRead++;
  385.                                 }
  386.                                 howMuchInBuffer -= result;
  387.                         }
  388.                         while(howMuchInBuffer > 0);
  389.                         printf("\" %d bytes%c", numBytesRead,
  390.                                 ((numBytesRead!=0)?'\n':'\r'));
  391.                 }
  392.                 else
  393.                 {
  394.                         printf("client: \"%s\" %d\r", userTextField, iterations++);
  395.                 }
  396.         }
  397.         return EXIT_SUCCESS;
  398. }
  399.  
  400. int getReadStatus(const SOCKET a_socket)
  401. {
  402.         // zero seconds, zero milliseconds. max time select call allowd to block
  403.         static const timeval instantSpeedPlease = {0,0};
  404.         fd_set a = {1, {a_socket}};
  405. /*
  406. 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.
  407. */
  408.         int result = select(0,&a,0,0,&instantSpeedPlease);
  409.         if(result == SOCKET_ERROR)
  410.         {
  411.                 result = WSAGetLastError();
  412.         }
  413.         if(result != 0 && result != 1)
  414.         {
  415.                 printf("select() error %d\n", result);
  416.                 return SOCKET_ERROR;
  417.         }
  418.         return result;
  419. }
  420.  
  421. void finalWSACleanup() // callback used to clean up sockets
  422. {
  423.         int result = WSACleanup();
  424.         if (result != 0)
  425.         {
  426.                 printf("WSACleanup() error %d\n", result);
  427.         }
  428. }
RAW Paste Data
Top