|
| 1 | +/** |
| 2 | + * @file |
| 3 | + * @author [NVombat](https://github.com/NVombat) |
| 4 | + * @brief Server-side implementation of [TCP Full Duplex |
| 5 | + * Communication](http://www.tcpipguide.com/free/t_SimplexFullDuplexandHalfDuplexOperation.htm) |
| 6 | + * @see tcp_full_duplex_server.c |
| 7 | + * |
| 8 | + * @details |
| 9 | + * The algorithm is based on the simple TCP client and server model. However, |
| 10 | + * instead of the server only sending and the client only receiving data, |
| 11 | + * The server and client can both send and receive data simultaneously. This is |
| 12 | + * implemented by using the `fork` function call so that in the server the child |
| 13 | + * process can recieve data and parent process can send data, and in the client |
| 14 | + * the child process can send data and the parent process can receive data. It |
| 15 | + * runs an infinite loop and can send and receive messages indefinitely until |
| 16 | + * the user exits the loop. In this way, the Full Duplex Form of communication |
| 17 | + * can be represented using the TCP server-client model & socket programming |
| 18 | + */ |
| 19 | + |
| 20 | +#include <arpa/inet.h> /// For the type in_addr_t and in_port_t |
| 21 | +#include <netdb.h> /// For structures returned by the network database library - formatted internet addresses and port numbers |
| 22 | +#include <netinet/in.h> /// For in_addr and sockaddr_in structures |
| 23 | +#include <stdint.h> /// For specific bit size values of variables |
| 24 | +#include <stdio.h> /// Variable types, several macros, and various functions for performing input and output |
| 25 | +#include <stdlib.h> /// Variable types, several macros, and various functions for performing general functions |
| 26 | +#include <string.h> /// Various functions for manipulating arrays of characters |
| 27 | +#include <sys/socket.h> /// For macro definitions related to the creation of sockets |
| 28 | +#include <sys/types.h> /// For definitions to allow for the porting of BSD programs |
| 29 | +#include <unistd.h> /// For miscellaneous symbolic constants and types, and miscellaneous functions |
| 30 | + |
| 31 | +#define PORT 10000 /// Define port over which communication will take place |
| 32 | + |
| 33 | +/** |
| 34 | + * @brief Utility function used to print an error message to `stderr`. |
| 35 | + * It prints `str` and an implementation-defined error |
| 36 | + * message corresponding to the global variable `errno`. |
| 37 | + * @returns void |
| 38 | + */ |
| 39 | +void error() |
| 40 | +{ |
| 41 | + perror("Socket Creation Failed"); |
| 42 | + exit(EXIT_FAILURE); |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * @brief Main function |
| 47 | + * @returns 0 on exit |
| 48 | + */ |
| 49 | +int main() |
| 50 | +{ |
| 51 | + /** Variable Declarations */ |
| 52 | + uint32_t sockfd, |
| 53 | + conn; ///< socket descriptors - Like file handles but for sockets |
| 54 | + char recvbuff[1024], |
| 55 | + sendbuff[1024]; ///< character arrays to read and store string data |
| 56 | + /// for communication |
| 57 | + |
| 58 | + struct sockaddr_in server_addr, |
| 59 | + client_addr; ///< asic structures for all syscalls and functions that |
| 60 | + /// deal with internet addresses. Structures for handling |
| 61 | + /// internet addresses |
| 62 | + socklen_t ClientLen; /// size of address |
| 63 | + |
| 64 | + /** |
| 65 | + * The TCP socket is created using the socket function |
| 66 | + * |
| 67 | + * AF_INET (Family) - it is an address family that is used to designate the |
| 68 | + * type of addresses that your socket can communicate with |
| 69 | + * |
| 70 | + * SOCK_STREAM (Type) - Indicates TCP Connection - A stream socket provides |
| 71 | + * for the bidirectional, reliable, sequenced, and unduplicated flow of data |
| 72 | + * without record boundaries. Aside from the bidirectionality of data flow, |
| 73 | + * a pair of connected stream sockets provides an interface nearly identical |
| 74 | + * to pipes |
| 75 | + * |
| 76 | + * 0 (Protocol) - Specifies a particular protocol to be used with the |
| 77 | + * socket. Specifying a protocol of 0 causes socket() to use an unspecified |
| 78 | + * default protocol appropriate for the requested socket type |
| 79 | + */ |
| 80 | + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
| 81 | + { |
| 82 | + error(); ///< Error if the socket descriptor has a value lower than 0 - |
| 83 | + /// socket wasnt created |
| 84 | + } |
| 85 | + |
| 86 | + /** |
| 87 | + * Server Address Information |
| 88 | + * |
| 89 | + * The bzero() function erases the data in the n bytes of the memory |
| 90 | + * starting at the location pointed to, by writing zeros (bytes |
| 91 | + * containing '\0') to that area |
| 92 | + * |
| 93 | + * We bind the server_addr to the internet address and port number thus |
| 94 | + * giving our socket an identity with an address and port where it can |
| 95 | + * listen for connections |
| 96 | + * |
| 97 | + * htons - The htons() function translates a short integer from host byte |
| 98 | + * order to network byte order |
| 99 | + * |
| 100 | + * htonl - The htonl() function translates a long integer from host byte |
| 101 | + * order to network byte order |
| 102 | + * |
| 103 | + * These functions are necessary so that the binding of address and port |
| 104 | + * takes place with data in the correct format |
| 105 | + */ |
| 106 | + bzero(&server_addr, sizeof(server_addr)); |
| 107 | + server_addr.sin_family = AF_INET; |
| 108 | + server_addr.sin_port = htons(PORT); |
| 109 | + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| 110 | + |
| 111 | + printf("Server is running...\n"); |
| 112 | + |
| 113 | + /** |
| 114 | + * This binds the socket descriptor to the server thus enabling the server |
| 115 | + * to listen for connections and communicate with other clients |
| 116 | + */ |
| 117 | + if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) |
| 118 | + { |
| 119 | + error(); /// If binding is unsuccessful |
| 120 | + } |
| 121 | + /** |
| 122 | + * This is to listen for clients or connections made to the server |
| 123 | + * |
| 124 | + * The limit is currently at 5 but can be increased to listen for |
| 125 | + * more connections |
| 126 | + * |
| 127 | + * It listens to connections through the socket descriptor |
| 128 | + */ |
| 129 | + listen(sockfd, 5); |
| 130 | + |
| 131 | + printf("Server is listening...\n"); |
| 132 | + |
| 133 | + /** |
| 134 | + * When a connection is found, a socket is created and connection is |
| 135 | + * accepted and established through the socket descriptor |
| 136 | + */ |
| 137 | + conn = accept(sockfd, (struct sockaddr *)NULL, NULL); |
| 138 | + |
| 139 | + printf("Server is connected...\n"); |
| 140 | + |
| 141 | + /** |
| 142 | + * Communication between client and server |
| 143 | + * |
| 144 | + * The bzero() function erases the data in the n bytes of the memory |
| 145 | + * starting at the location pointed to, by writing zeros (bytes |
| 146 | + * containing '\0') to that area. The variables are emptied and then |
| 147 | + * ready for use |
| 148 | + * |
| 149 | + * The fork function call is used to create a child and parent process |
| 150 | + * which run and execute code simultaneously |
| 151 | + * |
| 152 | + * The child process is used to receive data and after doing so |
| 153 | + * sleeps for 5 seconds to wait for the parent to send data |
| 154 | + * |
| 155 | + * The parent process is used to send data and after doing so |
| 156 | + * sleeps for 5 seconds to wait for the child to receive data |
| 157 | + * |
| 158 | + * The server and client can communicate indefinitely till one of them |
| 159 | + * exits the connection |
| 160 | + * |
| 161 | + * Since the exchange of information between the server and client takes |
| 162 | + * place simultaneously this represents FULL DUPLEX COMMUNICATION |
| 163 | + */ |
| 164 | + pid_t pid; |
| 165 | + pid = fork(); |
| 166 | + if (pid == 0) /// Value of 0 is for child process |
| 167 | + { |
| 168 | + while (1) |
| 169 | + { |
| 170 | + bzero(&recvbuff, sizeof(recvbuff)); |
| 171 | + recv(conn, recvbuff, sizeof(recvbuff), 0); |
| 172 | + printf("\nCLIENT : %s\n", recvbuff); |
| 173 | + sleep(5); |
| 174 | + // break; |
| 175 | + } |
| 176 | + } |
| 177 | + else /// Parent process |
| 178 | + { |
| 179 | + while (1) |
| 180 | + { |
| 181 | + bzero(&sendbuff, sizeof(sendbuff)); |
| 182 | + printf("\nType message here: "); |
| 183 | + fgets(sendbuff, 1024, stdin); |
| 184 | + send(conn, sendbuff, strlen(sendbuff) + 1, 0); |
| 185 | + printf("\nMessage Sent!\n"); |
| 186 | + sleep(5); |
| 187 | + // break; |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + /// Close socket |
| 192 | + close(sockfd); |
| 193 | + printf("Server is offline...\n"); |
| 194 | + return 0; |
| 195 | +} |
0 commit comments