>

Socket Programming Tips

#Socket Programming with UDP

#Server Side

  1. Include all necessary header files.
#include <sys/socket.h> // Socket interface
#include <arpa/inet.h> // Internet protocol
#include <string.h> // strlen
#include <unistd.h> // close, etc.
#include <errno.h> // Get error number
  1. Create the socket and get its file descriptor.
/* 1. Create socket */
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                 // use IPv4  use UDP

AF_INET specifies to use IPv4 and SOCK_DGRAM specifies to use UDP.

  1. Create the server address to accept connections.
/* 2. Construct our address */
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET; // use IPv4
servaddr.sin_addr.s_addr = INADDR_ANY; // accept all connections
                        // same as inet_addr("0.0.0.0") 
                                 // "Address string to network bytes"
// Set receiving port
int PORT = 8080;
servaddr.sin_port = htons(PORT); // Big endian

Networking is always done in big-endian format, so htons is required to change the byte ordering from host to network.

  1. Bind the socket with the server address.
/* 3. Let operating system know about our config */
int did_bind = bind(sockfd, (struct sockaddr*) &servaddr, 
                    sizeof(servaddr));
// Error if did_bind < 0 :(
if (did_bind < 0) return errno;
  1. Create a buffer to store messages from clients.

Also, create a temporary client address struct to inspect client information (IP address, source port).

/* 4. Create buffer to store incoming data */
int BUF_SIZE = 1024;
char client_buf[BUF_SIZE];
struct sockaddr_in clientaddr = {0}; // Same information, but about client
socklen_t clientsize = sizeof(clientaddr);
  1. Listen to messages from clients. Feel free to put these steps in a loop to create an ever-listening server.
/* 5. Listen for data from clients */
int bytes_recvd = recvfrom(sockfd, client_buf, BUF_SIZE, 
                        // socket  store data  how much
                           0, (struct sockaddr*) &clientaddr, 
                           &clientsize);
// Execution will stop here until `BUF_SIZE` is read or termination/error
// Error if bytes_recvd < 0 :(
if (bytes_recvd < 0) return errno;
  1. Inspect the client IP address, source port, and the sent message.
/* 6. Inspect data from client */
char* client_ip = inet_ntoa(clientaddr.sin_addr);
                // "Network bytes to address string"
int client_port = ntohs(clientaddr.sin_port); // Little endian

The client message is in client_buf.

  1. Send a message back to the client (place the message in some buffer, let’s say server_buf).
/* 7. Send data back to client */
char server_buf[] = "Hello world!";
int did_send = sendto(sockfd, server_buf, strlen(server_buf), 
                   // socket  send data   how much to send
                      0, (struct sockaddr*) &clientaddr, 
                   // flags   where to send
                      sizeof(clientaddr));
  1. On interrupt (or on any exit), close the server.
/* 8. You're done! Terminate the connection */     
close(sockfd);

#Client Side

  1. Include all necessary header files.
#include <sys/socket.h> // Socket interface
#include <arpa/inet.h> // Internet protocol
#include <string.h> // strlen
#include <unistd.h> // close, etc.
#include <errno.h> // Get error number
  1. Create the socket and get its file descriptor.
/* 1. Create socket */
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
                 // use IPv4  use UDP

AF_INET specifies to use IPv4 and SOCK_DGRAM specifies to use UDP.

  1. Create the server address to connect to the server.
/* 2. Construct our address */
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET; // use IPv4
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Set receiving port
int PORT = 8080;
serveraddr.sin_port = htons(PORT); // Big endian

Networking is always done in big-endian format, so htons is required to change the byte ordering from host to network.

  1. Send a message to the server (place the message in some buffer, let’s say client_buf).
/* 3. Send data to server */
char client_buf[] = "Hello world!";
int did_send = sendto(sockfd, client_buf, strlen(client_buf),
                   // socket  send data   how much to send
                      0, (struct sockaddr*) &serveraddr,
                   // flags   where to send
                      sizeof(serveraddr));
if (did_send < 0) return errno;
  1. Create a buffer to store messages from the server.
/* 4. Create buffer to store incoming data */
int BUF_SIZE = 1024;
char server_buf[BUF_SIZE];
socklen_t serversize = sizeof(struct sockaddr_in); // Temp buffer for recvfrom API
  1. Listen to messages from server. Feel free to put these steps in a loop to create an ever-listening client.
/* 5. Listen for response from server */
int bytes_recvd = recvfrom(sockfd, server_buf, BUF_SIZE, 
                        // socket  store data  how much
                           0, (struct sockaddr*) &serveraddr, 
                           &serversize);
// Execution will stop here until `BUF_SIZE` is read or termination/error
// Error if bytes_recvd < 0 :(
if (bytes_recvd < 0) return errno;
// Print out data
write(1, server_buf, bytes_recvd);
  1. On interrupt (or on any exit), close the server.
/* 8. You're done! Terminate the connection */     
close(sockfd);

#Aside: Using non-blocking sockets

When using recvfrom, the default behavior is to block the current thread. However, you’ll want these functions to simply check if there’s any data available from the other host and continue to process other data (maybe read from standard in—which you can also make non-blocking).

We can use the fcntl or Control Open File Descriptors system call to modify our socket to be non-blocking—if there’s no data, it simply continues execution.

  1. Include another header file.
#include <fcntl.h>
  1. After setting up your socket with the socket syscall, modify it using the fcntl syscall to be non-blocking.
// Setup fd set for nonblock
int flags = fcntl(sockfd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(sockfd, F_SETFL, flags);
  1. Now when using recvfrom, the call will continue even if there’s no data.
while (true) {
  /* Do some processing here */
	
  int bytes_recvd = recvfrom(sockfd, client_buf, BUF_SIZE, 
	                        // socket  store data  how much
	                           0, (struct sockaddr*) &clientaddr, 
	                           &clientsize);
  // No data yet, we can continue processing at the top of this loop
  if (bytes_recvd <= 0) continue;              
	                      
  /* Data available; we can now process the data */                  
}

#Socket Programming with TCP

#Server Side

  1. Include all necessary header files.
#include <sys/socket.h> // Socket interface
#include <arpa/inet.h> // Internet protocol
#include <string.h> // strlen
#include <unistd.h> // close, etc.
#include <errno.h> // Get errorno
  1. Create the socket and get its file descriptor.
/* 1. Create socket */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
                 // use IPv4  use TCP

AF_INET specifies to use IPv4 and SOCK_STREAM specifies to use TCP.

  1. Create the server address to accept connections.
/* 2. Construct our address */
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET; // use IPv4
servaddr.sin_addr.s_addr = INADDR_ANY; // accept all connections
                        // same as inet_addr("0.0.0.0") 
                                 // "Address string to network bytes"
// Set receiving port
int PORT = 8080;
servaddr.sin_port = htons(PORT); // Big endian

Networking is always done in big-endian format, so htons is required to change the byte ordering from host to network.

  1. Bind the socket with the server address.
/* 3. Let operating system know about our config */
int did_bind = bind(sockfd, (struct sockaddr*) &servaddr, 
                    sizeof(servaddr));
// Error if did_bind < 0 :(
if (did_bind < 0) return errno;
  1. Create a buffer to store messages from clients.

Also, create a temporary client address struct to inspect client information (IP address, source port).

/* 4. Create buffer to store incoming data */
int BUF_SIZE = 1024;
char client_buf[BUF_SIZE];
struct sockaddr_in clientaddr = {0}; // Same information, but about client
socklen_t clientsize = sizeof(clientaddr);
  1. Listen for new clients by marking this socket as passive.
/* 5. Listen for new clients */
int did_find_client = listen(sockfd, 1);
                          // socket  flags
// Error if did_find_client < 0 :(
if (did_find_client < 0) return errno;
  1. Accept the client connection by creating a new socket for this client. This will block the current thread until a client attempts to connect. This is the second and third parts of the three way handshake. Feel free to put these steps in a loop to create an ever listening server.
/* 6. Accept client connection */
int clientfd = accept(sockfd,
                   // socket
                      (struct sockaddr*) &clientaddr,
                   // client info
                      &clientsize);
// Error if clientfd < 0 :(
if (clientfd < 0) return errno;
  1. Inspect the client IP address and the source port.
/* 7. Inspect client info */
char* client_ip = inet_ntoa(clientaddr.sin_addr);
                // "Network bytes to address string"
int client_port = ntohs(clientaddr.sin_port); // Little endian
  1. Listen to messages from the client. Feel free to put these steps in a loop to keep the TCP connection alive.
/* 8. Listen for messages from client */
int bytes_recvd = recv(clientfd, client_buf, BUF_SIZE, 0);
                    // socket  store data  how much flags
                    
// Execution will stop here until `BUF_SIZE` is read or termination/error
// Error if bytes_recvd < 0 :(
if (bytes_recvd < 0) return errno;
  1. Send a message back to the client (place the message in some buffer, let’s say server_buf).
/* 7. Send data back to client */
char server_buf[] = "Hello world!";
int did_send = send(clientfd, server_buf, strlen(server_buf), 0);
                 // socket  send data   how much to send  flags
if (did_send < 0) return errno;
  1. If the application has no need to send messages to this client, close the connection.
/* 10. Terminate the client connection */
close(clientfd);
  1. On interrupt (or on any exit), close the server.
/* 8. You're done! Terminate the connection */     
close(sockfd);

#Client Side

  1. Include all necessary header files.
#include <sys/socket.h> // Socket interface
#include <arpa/inet.h> // Internet protocol
#include <string.h> // strlen
#include <unistd.h> // close, etc.
#include <errno.h> // Get errorno
  1. Create the socket and get its file descriptor.
/* 1. Create socket */
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
                 // use IPv4  use TCP

AF_INET specifies to use IPv4 and SOCK_STREAM specifies to use TCP.

  1. Create the server address to connect to the server.
/* 2. Construct our address */
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET; // use IPv4
serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
// Set receiving port
int PORT = 8080;
serveraddr.sin_port = htons(PORT); // Big endian

Networking is always done in big-endian format, so htons is required to change the byte ordering from host to network.

  1. Attempt to connect to the server. This is the first, second, and third steps of the three way handshake.
int did_connect = connect(sockfd, (struct sockaddr*) &serveraddr, 
                       // socket   server info
                          sizeof(serveraddr));
// Error if did_connect < 0 :(
if (did_connect < 0) return errno;
  1. Send a message to the server (place the message in some buffer, let’s say client_buf). Feel free to put these steps in a loop to keep the TCP connection alive.
/* 3. Send data to server */
char client_buf[] = "Hello world!";
int did_send = send(sockfd, client_buf, strlen(client_buf), 0);
                 // socket  send data   how much to send  flags
// Error if did_send < 0 :(
if (did_send < 0) return errno;
  1. Create a buffer to store messages from the server.
/* 4. Create buffer to store incoming data */
int BUF_SIZE = 1024;
char server_buf[BUF_SIZE];
  1. Listen to messages from server. Feel free to put these steps in a loop to create an ever-listening client.
/* 6. Listen for response from server */
int bytes_recvd = recv(sockfd, server_buf, BUF_SIZE, 0);
                    // socket  store data  how much  flags
// Execution will stop here until `BUF_SIZE` is read or termination/error
// Error if bytes_recvd < 0 :(
if (bytes_recvd < 0) return errno;
  1. On interrupt (or on any exit), close the server.
/* 8. You're done! Terminate the connection */     
close(sockfd);

#Source Code

#UDP

server.c

client.c

#TCP

server_tcp.c

client_tcp.c