tcp_udp
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tcp_udp [2017/04/30 23:51] – dwallace | tcp_udp [2017/05/02 22:45] (current) – dwallace | ||
---|---|---|---|
Line 10: | Line 10: | ||
{{ dylanw: | {{ dylanw: | ||
\\ | \\ | ||
- | The photo above depicts the differences between TCP & UDP connections which allow you to create different forms of communications. The big picture problem is high-latency or low-bandwidth telerobotics. Solving this partially or completely is important because it will allow remote operation of space robotics, disaster relief, etc. This tutorial shows you how to create a TCP client & server, and a UDP talker & listener, and takes approximately | + | The photo above depicts the differences between TCP & UDP connections which allow you to create different forms of communications. The big picture problem is high-latency or low-bandwidth telerobotics. Solving this partially or completely is important because it will allow remote operation of space robotics, disaster relief, etc. This tutorial shows you how to create a TCP client & server, and a UDP talker & listener, and takes approximately |
\\ | \\ | ||
===== Motivation and Audience ===== | ===== Motivation and Audience ===== | ||
Line 25: | Line 25: | ||
* This tutorial may also attract readers who are interested in web programming | * This tutorial may also attract readers who are interested in web programming | ||
</fc> | </fc> | ||
- | \\ | + | \\ \\ |
The rest of this tutorial is presented as follows: | The rest of this tutorial is presented as follows: | ||
* [[tcp_udp# | * [[tcp_udp# | ||
Line 33: | Line 33: | ||
===== Protocol Introductions ===== | ===== Protocol Introductions ===== | ||
+ | |||
+ | For a detailed overview of TCP & UDP communications, | ||
+ | |||
+ | ==== Internet vs Intranet ==== | ||
+ | |||
+ | Networking is a term that describes connecting multiple computers together into a " | ||
+ | |||
+ | ==== IP Addresses ==== | ||
+ | |||
+ | If you use the internet at all, you have probably heard of the term IP address. IP addresses are simply a number that describes a computer connected to the Internet. IP stands for Internet Protocol. | ||
+ | |||
+ | IP addresses have two forms: | ||
+ | |||
+ | * IPv4: the older, more common protocol; has much less IP addresses available | ||
+ | * IPv6: the modern, increasingly common protocol; has many many more addresses available | ||
+ | |||
+ | IPv4 was the first worldwide Internet protocol, and is still the most common protocol used today, due to its simplicity. IPv4 uses 4 unique bytes to describe an IP address, each with a range of 0-255, giving a total address of 32 bits. This amounts to around 4.3 billion IP addresses globally, which is not enough for the growing amount of computers in the modern world. This is what led to the development of IPv6 in the late 1990' | ||
+ | |||
+ | IPv6 is becoming more and more popular, due to its enormous amount of IP addresses available. IPv6 uses 8 unique 16-bit sections, totalling a 128-bit address. This is nearly 3.4 x 10^38 unique addresses, more than enough for every atom on Earth to have its own IP address. This has essentially solved the IP address problem for the foreseeable future. | ||
+ | |||
+ | IPv4 example: 192.168.0.225 | ||
+ | IPv6 example: 2001: | ||
+ | |||
+ | ==== Sockets ==== | ||
+ | |||
+ | Sockets are the way that we create and utilize connections when programming communications. There are two types of sockets: datagram (UDP) and stream (TCP). Sockets are tied to individual ports, which are virtual sections of a computer that are dedicated to individual processes. This allows multiple applications to communicate with each other and other networks at the same time. Ports are 16-bit numbers, giving an average computer around 65,000 of them. However, the first 1,000 are reserved for important processes. | ||
+ | |||
+ | {{ dylanw: | ||
+ | |||
+ | The picture above shows the concept of data encapsulation. In computer networks, there are many different protocols to deal with. In order to make the job easier on the program, we simplify the process by " | ||
+ | |||
+ | 1. Application | ||
+ | 2. Presentation | ||
+ | 3. Session | ||
+ | 4. Transport | ||
+ | 5. Network | ||
+ | 6. Data Link | ||
+ | 7. Physical | ||
+ | |||
+ | Where the application is what is directly using the data, such as TFTP, and the physical is the last line of sending/ | ||
+ | |||
+ | ==== TCP ==== | ||
+ | |||
+ | TCP stands for Transmission Control Protocol, and describes a connection that send a constant **stream** of bytes. TCP establishes a direct connection between the two computers, and send the bytes over this direct connection. The sending computer is called the **server**, and it waits for a computer to connect to a defined port. The receiving computer is called a **client**, and it establishes the connection with the server and receives the requested message. TCP is the most commonly used transmission protocol, because it ensures that data is received if sent. This is why TCP/IP is the protocol used for the Internet, especially for browsing the web. When you load a webpage, your computer establishes a TCP/IP connection with the server, and the server sends that webpage directly to your computer, ensuring a safe, complete delivery. | ||
+ | |||
+ | ==== UDP ==== | ||
+ | |||
+ | UDP stands for the User Datagram Protocol, and describes a connection that sends a **packet** of bytes. UDP sends a predefined amount of data all at once, and it is **hopefully** received at the other end. The sending computer is called the **talker**, and it sends the message to a specific IP address and port. The receiving computer is called the **listener**, | ||
===== TCP Client & Server ===== | ===== TCP Client & Server ===== | ||
+ | In this section we will go over the C code for the TCP client and server. Please note that this code is meant for sockets on Unix systems, and therefore may not work on Windows without some modifications. For these modifications, | ||
+ | |||
+ | The complete code for the client and server can be found below: | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | ==== Server ==== | ||
+ | |||
+ | Let's break down the server code. The server is a program that binds to a certain port, and send a predefined message to any client that connects to it. The first block is where we include all the necessary libraries and define essential constants. | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define PORT " | ||
+ | |||
+ | #define BACKLOG 10 // how many pending connections queue will hold | ||
+ | </ | ||
+ | |||
+ | Next, we will define a function to read the dead processes from previous programs, in order to free up memory. Don't worry too much about the specifics. | ||
+ | |||
+ | <code c> | ||
+ | // Function to reap all dead processes | ||
+ | void sigchld_handler(int s) | ||
+ | { | ||
+ | // waitpid() might overwrite errno, so we save and restore it: | ||
+ | int saved_errno = errno; | ||
+ | |||
+ | while(waitpid(-1, | ||
+ | |||
+ | errno = saved_errno; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Next, we will define a function to get the IP address regardless of IP version. | ||
+ | |||
+ | <code c> | ||
+ | // Function to get sockaddr, IPv4 or IPv6 | ||
+ | void *get_in_addr(struct sockaddr *sa) | ||
+ | { | ||
+ | if (sa-> | ||
+ | return & | ||
+ | } | ||
+ | |||
+ | return & | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now, we will get to the main( ). We will start off with declaring all of our objects, and filling our structs with important info. | ||
+ | |||
+ | <code c> | ||
+ | int sockfd, new_fd; | ||
+ | struct addrinfo hints, *servinfo, *p; //Holds IP address info | ||
+ | struct sockaddr_storage their_addr; // Connector' | ||
+ | socklen_t sin_size; // Holds socket size | ||
+ | struct sigaction sa; // Holds data for reaping dead processes | ||
+ | int yes = 1; // Boolean reference | ||
+ | char s[INET6_ADDRSTRLEN]; | ||
+ | int rv; // Holds error status for getaddrinfo | ||
+ | |||
+ | memset(& | ||
+ | hints.ai_family = AF_UNSPEC; // Set family to either IPv4 or IPv6, we don't care | ||
+ | hints.ai_socktype = SOCK_STREAM; | ||
+ | hints.ai_flags = AI_PASSIVE; // Use my IP | ||
+ | </ | ||
+ | |||
+ | Then, we will get the necessary info from our struct using getaddrinfo. | ||
+ | |||
+ | <code c> | ||
+ | if ((rv = getaddrinfo(NULL, | ||
+ | fprintf(stderr, | ||
+ | return 1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now that our servinfo struct is filled, we will loop through the linked-list of them until we successfully create and bind to a socket. | ||
+ | |||
+ | <code c> | ||
+ | for(p = servinfo; p != NULL; p = p-> | ||
+ | if ((sockfd = socket(p-> | ||
+ | p-> | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | if (setsockopt(sockfd, | ||
+ | sizeof(int)) == -1) { // If error in getting the option | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | if (bind(sockfd, | ||
+ | close(sockfd); | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now that our server is bound to a port successfully, | ||
+ | |||
+ | <code c> | ||
+ | freeaddrinfo(servinfo); | ||
+ | |||
+ | if (p == NULL) { // If no port bound to p | ||
+ | fprintf(stderr, | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | if (listen(sockfd, | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now that our server has validated the connection, we will reap all dead processes, and if successful, print that we are now awaiting a connection. | ||
+ | |||
+ | <code c> | ||
+ | sa.sa_handler = sigchld_handler; | ||
+ | sigemptyset(& | ||
+ | sa.sa_flags = SA_RESTART; | ||
+ | if (sigaction(SIGCHLD, | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | printf(" | ||
+ | </ | ||
+ | |||
+ | In order to accept a connection to the server on the defined socket/ | ||
+ | |||
+ | <code c> | ||
+ | while(1) { // Main accept() loop | ||
+ | sin_size = sizeof their_addr; | ||
+ | new_fd = accept(sockfd, | ||
+ | if (new_fd == -1) { // If error during accept | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | inet_ntop(their_addr.ss_family, | ||
+ | get_in_addr((struct sockaddr *)& | ||
+ | s, sizeof s); // Translate IP address into string form | ||
+ | printf(" | ||
+ | |||
+ | if (!fork()) { // This is the child process | ||
+ | close(sockfd); | ||
+ | if (send(new_fd, | ||
+ | perror(" | ||
+ | close(new_fd); | ||
+ | exit(0); | ||
+ | } | ||
+ | close(new_fd); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Note, don't forget that all of the code after the first two blocks belongs in main( ), and that we must return 0 at the end of main( ). | ||
+ | |||
+ | Here is what it looks like when we launch the server: | ||
+ | |||
+ | {{ dylanw: | ||
+ | \\ | ||
+ | And here is what it looks like when something connects to the server: | ||
+ | |||
+ | {{ dylanw: | ||
+ | |||
+ | ==== Client ==== | ||
+ | |||
+ | Now, let's go over the client. The client is a program that connects to server port at a given IP address, and receives the message that the server sends. This program is much simpler, as it only need to handle one connection during its run. We start out with the includes and the constant declarations, | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #include < | ||
+ | |||
+ | #define PORT " | ||
+ | |||
+ | #define MAXDATASIZE 100 // Max number of bytes we can get at once | ||
+ | </ | ||
+ | |||
+ | Now that we have taken care of these, we define the same get_in_addr function from the server. | ||
+ | |||
+ | <code c> | ||
+ | // Get sockaddr, IPv4 or IPv6 | ||
+ | void *get_in_addr(struct sockaddr *sa) | ||
+ | { | ||
+ | if (sa-> | ||
+ | return & | ||
+ | } | ||
+ | |||
+ | return & | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now we will enter the main( ) function, which takes command line arguments for the IP address of the server. At the beginning of main( ), we will declare some important structures, check the command line arguments, and fill the IP address struct with basic info. | ||
+ | |||
+ | <code c> | ||
+ | int sockfd, numbytes; // Stores our socket descriptor, and number of bytes read | ||
+ | char buf[MAXDATASIZE]; | ||
+ | struct addrinfo hints, *servinfo, *p; // Stores our IP address info | ||
+ | int rv; // Used for error checking | ||
+ | char s[INET6_ADDRSTRLEN]; | ||
+ | |||
+ | if (argc != 2) { // Check CL argument count | ||
+ | fprintf(stderr," | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | memset(& | ||
+ | hints.ai_family = AF_UNSPEC; // Don't care if IPv4 or IPv6 | ||
+ | hints.ai_socktype = SOCK_STREAM; | ||
+ | |||
+ | </ | ||
+ | |||
+ | After this, we use the getaddrinfo function just like in the server. | ||
+ | |||
+ | <code c> | ||
+ | if ((rv = getaddrinfo(argv[1], | ||
+ | fprintf(stderr, | ||
+ | return 1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now, we do a similar for loop to the server, where we search through the servinfo linked-list until we properly create and connect to the socket. | ||
+ | |||
+ | <code c> | ||
+ | // Loop through all the results and connect to the first we can | ||
+ | for(p = servinfo; p != NULL; p = p-> | ||
+ | if ((sockfd = socket(p-> | ||
+ | p-> | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | if (connect(sockfd, | ||
+ | close(sockfd); | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now, to ensure we are properly connected, we will check to make sure anything actually connected. Then, we will convert the IP address to a string, and then print it along with the connection message. | ||
+ | |||
+ | <code c> | ||
+ | if (p == NULL) { // If nothing ever connected | ||
+ | fprintf(stderr, | ||
+ | return 2; | ||
+ | } | ||
+ | |||
+ | inet_ntop(p-> | ||
+ | s, sizeof s); // Convert IP address into a string | ||
+ | printf(" | ||
+ | |||
+ | freeaddrinfo(servinfo); | ||
+ | </ | ||
+ | |||
+ | Now that we have ensured a proper connection, we will receive the message from the server, add a null-terminator to it, and print the message that was received. Finally, we will close the socket. | ||
+ | |||
+ | <code c> | ||
+ | if ((numbytes = recv(sockfd, | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | buf[numbytes] = ' | ||
+ | |||
+ | printf(" | ||
+ | |||
+ | close(sockfd); | ||
+ | |||
+ | </ | ||
+ | |||
+ | Just as with the server, we must ensure that everything after the first two blocks is in the main( ) function, and that we return 0 at the end. | ||
+ | |||
+ | Here is what it looks like when the client is launched, and receives a message: | ||
+ | |||
+ | {{ dylanw: | ||
===== UDP Talker & Listener ===== | ===== UDP Talker & Listener ===== | ||
+ | Now that we have established the TCP server & client, we can move onto the UDP talker & listener. The code for these is fairly similar, with only some minor changes. | ||
+ | |||
+ | The code for the UDP Talker & Listener can be found below: | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | ==== Listener ==== | ||
+ | |||
+ | This listener has very similar style to the TCP server we created. The listener is a program that binds to a predefined port, and listens to any messages received on that port, like a " | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define MYPORT " | ||
+ | |||
+ | #define MAXBUFLEN 100 // The maximum receiving buffer | ||
+ | </ | ||
+ | |||
+ | And again, the get_in_addr function. | ||
+ | |||
+ | <code c> | ||
+ | // Function to get sockaddr, IPv4 or IPv6 | ||
+ | void *get_in_addr(struct sockaddr *sa) | ||
+ | { | ||
+ | if (sa-> | ||
+ | return & | ||
+ | } | ||
+ | |||
+ | return & | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now, we move onto the main( ) function. We start out with the usual data declarations, | ||
+ | |||
+ | <code c> | ||
+ | int sockfd; // Socket descriptor | ||
+ | struct addrinfo hints, *servinfo, *p; // Used to store IP address info | ||
+ | int rv; // Used for error checking | ||
+ | int numbytes; // Number of bytes received | ||
+ | struct sockaddr_storage their_addr; // Store the talker IP address | ||
+ | char buf[MAXBUFLEN]; | ||
+ | socklen_t addr_len; // IP address length | ||
+ | char s[INET6_ADDRSTRLEN]; | ||
+ | |||
+ | memset(& | ||
+ | hints.ai_family = AF_UNSPEC; // Don't care if IPv4 or IPv6 (set to AF_INET to force IPv4) | ||
+ | hints.ai_socktype = SOCK_DGRAM; // UDP type | ||
+ | hints.ai_flags = AI_PASSIVE; // Use my IP to listen to the socket | ||
+ | |||
+ | if ((rv = getaddrinfo(NULL, | ||
+ | fprintf(stderr, | ||
+ | return 1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Then we use a for loop to search through the servinfo linked-list, | ||
+ | |||
+ | <code c> | ||
+ | // Loop through all the results and bind to the first we can | ||
+ | for(p = servinfo; p != NULL; p = p-> | ||
+ | if ((sockfd = socket(p-> | ||
+ | p-> | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | if (bind(sockfd, | ||
+ | close(sockfd); | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now, we check to see if anything was actually created and bound, and if so, free up the structure and print the waiting message. | ||
+ | |||
+ | <code c> | ||
+ | if (p == NULL) { // If nothing ever bound to the socket | ||
+ | fprintf(stderr, | ||
+ | return 2; | ||
+ | } | ||
+ | |||
+ | freeaddrinfo(servinfo); | ||
+ | |||
+ | printf(" | ||
+ | </ | ||
+ | |||
+ | Finally, we attempt to receive a message, waiting until one is received, and then print where the message was received from, how many bytes were sent, and what the message was. Finally, we close the socket. | ||
+ | |||
+ | <code c> | ||
+ | addr_len = sizeof their_addr; | ||
+ | if ((numbytes = recvfrom(sockfd, | ||
+ | (struct sockaddr *)& | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | printf(" | ||
+ | inet_ntop(their_addr.ss_family, | ||
+ | get_in_addr((struct sockaddr *)& | ||
+ | s, sizeof s)); // Print received message and converted IP address | ||
+ | printf(" | ||
+ | buf[numbytes] = ' | ||
+ | printf(" | ||
+ | |||
+ | close(sockfd); | ||
+ | </ | ||
+ | |||
+ | As with the others, make sure to include all of this in main( ), and return 0 at the end. | ||
+ | |||
+ | Here is what it looks like when the listener is launched: | ||
+ | |||
+ | {{ dylanw: | ||
+ | \\ | ||
+ | And here is what it looks like when the listener receives a message: | ||
+ | |||
+ | {{ dylanw: | ||
+ | |||
+ | ==== Talker ==== | ||
+ | |||
+ | The talker is probably the simplest program of all of these, with just 65 lines of code. The talker is a program that send given message to a predefined port on the given IP address. It doesn' | ||
+ | |||
+ | <code c> | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define SERVERPORT " | ||
+ | </ | ||
+ | |||
+ | Unlike all of the other programs, no need to define a get_in_addr function, because we are simply sending a UDP packet to the given IP address at the defined port. | ||
+ | |||
+ | So we move onto the main( ) function, with command line arguments. We start off with the usual: declaring structures, checking argument count, filling out the IP address structure, and calling getaddrinfo for the given IP address. | ||
+ | |||
+ | <code c> | ||
+ | int sockfd; // Socket descriptor | ||
+ | struct addrinfo hints, *servinfo, *p; // Structs to store IP address info | ||
+ | int rv; // Error checking | ||
+ | int numbytes; // Number of bytes to send | ||
+ | |||
+ | if (argc != 3) { // Check command line arguments | ||
+ | fprintf(stderr," | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | memset(& | ||
+ | hints.ai_family = AF_UNSPEC; // We don't care if IPv4 or IPv6 (set to AF_INET to force IPv4) | ||
+ | hints.ai_socktype = SOCK_DGRAM; // UDP type | ||
+ | |||
+ | if ((rv = getaddrinfo(argv[1], | ||
+ | fprintf(stderr, | ||
+ | return 1; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Next, we loop through the servinfo linked-list trying to create a socket, and then check if we actually created one. | ||
+ | |||
+ | <code c> | ||
+ | // Loop through all the results and make a socket | ||
+ | for(p = servinfo; p != NULL; p = p-> | ||
+ | if ((sockfd = socket(p-> | ||
+ | p-> | ||
+ | perror(" | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | break; | ||
+ | } | ||
+ | |||
+ | if (p == NULL) { // Check if anything actually ever was created | ||
+ | fprintf(stderr, | ||
+ | return 2; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Finally, we attempt to send the message packet to the IP address given and the port defined, free the IP address structure, print the message detailing the number of bytes sent, and finally close the socket. | ||
+ | |||
+ | <code c> | ||
+ | if ((numbytes = sendto(sockfd, | ||
+ | | ||
+ | perror(" | ||
+ | exit(1); | ||
+ | } | ||
+ | |||
+ | freeaddrinfo(servinfo); | ||
+ | |||
+ | printf(" | ||
+ | close(sockfd); | ||
+ | </ | ||
+ | |||
+ | As usual, make sure that the code besides the first block is within main( ), with Cl arguments, and you return 0 at the end. | ||
+ | |||
+ | Here is what it looks like when the talker is launched, and sends a message: | ||
+ | |||
+ | {{ dylanw: | ||
+ | |||
+ | |||
+ | This is basically it. Once you understand the basic network calls, and the looping structure, then it should be very easy to implement this yourself. Try out the program given yourself, and then try writing your own, and see if you can make it more efficient, or unique in your own way. Again, this is just the basics, and more difficult concepts will be covered in the future. | ||
===== Final Words ===== | ===== Final Words ===== |
tcp_udp.1493621490.txt.gz · Last modified: 2017/04/30 23:51 by dwallace