Hands-On TCP Sockets with C++ & Vim on Ubuntu

TCP | socket | Ubuntu | Linux | Vim | Makefile

Yu-Cheng (Morton) Kuo
Nerd For Tech
10 min readDec 19, 2023

--

Code on GitHub: Link to Repository

To familiarize myself with TCP & sockets, I decided to do create a Linux environment (Ubuntu) as the server and use my laptop (Windows 10) as the client, though this is just the first step toward TCP sockets. Let’s enjoy the delight of learning!

(1) Creating Linux Environment

I chose the VPS on Vultr to set up an Ubuntu Server.

Here’s the Chinese resources I referred to when setting up the Ubuntu server on Vultr, but you can easily find the English counterparts on YouTube.

  1. 【2023】 Vultr 教學 — 便宜高性能的VPS主機 (附數據與常用測試腳本)
  2. [Ubuntu][Linux][教學] 安裝設定#04–1 在Vultr VPS上架設Ubuntu Linux 主機

Below is the information of my server, it costs me $6 USD/month. Note that I choose Ubuntu 20.04 LTS x64, which is the oldest version of Ubuntu I could select for reliability.

After setting up the Ubuntu, install PuTTy and follow the steps in the video [Ubuntu][Linux][教學] 安裝設定#04–1 在Vultr VPS上架設Ubuntu Linux 主機 to get to the step as below.

Now, type the password and enter it.

  • Heads-Up: you can’t copy the password and paste it here.

Next, you may change the password by the following line of code.

root@socketDemo:~# sudo passwd

(2) Setting Up the Ubuntu Server

Now that you sucessfully get into the Ubuntu CLI, let’s set up the server.

// 1. Update the package lists
sudo apt-get update

// 2. Upgrade all your installed packages to their latest versions
sudo apt-get upgrade

// 3. Install a C++ compiler and other essential development tools
sudo apt-get install build-essential

// 4. Install a Code Editor
sudo apt-get install vim

// 5. Create a new file for your C++ code
vim mySocketProgram.cpp

// 6. Compile and Run Your Program
g++ -o mySocketProgram mySocketProgram.cpp

// 7. Run your program
./mySocketProgram

// 8. (Optional) Use Version Control
sudo apt-get install git

(3) hello.cpp

// hello.cpp
#include <iostream>

int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}

As usual, let’s start with “Hello, World”

// 1. Create a new file and get into it
vim hello.cpp

// 2. Copy hello.cpp above and right click in the CLI, which will do the paste
// Copy and paste operations in Linux CLI =>
// Copy: ctrl + shift + v
// Paste and enter: right click

// 3. Save the file and quit (Or you can use :wq)
:w
:q

// 4. Print out the file
cat hello.cpp

// 5. Compile (Or g++ -o hello hello.cpp)
g++ hello.cpp -o hello

// 6. Run
./hello

(4) Basic Vim Commands

:w // Save the current file
:q // Quit Vim (fails if there are unsaved changes)
:wq // Save the current file and quit Vim
:%d // Delete all lines in the file

ctrl + c // Quit insert mode, go back to Normal mode

shift + i // Insert text before the first non-blank character of the line (capital I)
Shift + I // Same as shift + i, goes into insert mode at the beginning of the line
Shift + a // Append text at the end of the line (capital A)
shift + A // Same as Shift + a, goes into insert mode at the end of the line
i // Insert text before the cursor
a // Append text after the cursor
o // Open a new line below the current line and enter insert mode
O // Open a new line above the current line and enter insert mode

yy // Yank (copy) the current line
y2 // (Incomplete command) Yank (copy) the next two lines, including the current one
shift + p // Put (paste) the yanked or deleted text before the cursor line (capital P)
p // Put (paste) the yanked or deleted text after the cursor or the current line

dd // Delete (cut) the current line

u // Undo the last operation
ctrl + r // Redo the last undone operation

:x // Save and quit (similar to :wq, but only writes if changes are made)
ZZ // Save and quit (similar to :x, but as a keystroke command)

Setup of the .vimrc file:

set cindent
set expandtab " Use spaces for indentation
set tabstop=4 " Number of spaces for a tab stop
set shiftwidth=4 " Number of spaces for auto-indenting
set number

autocmd FileType make set noexpandtab tabstop=4 shiftwidth=4

(5) Basic Linux Commands

Now, let’s create a new folder, and move all the stuff so far into the new folder.

ls // List all the stuff in the current directory
mkdir NewFolder // Create a new folder
ls // Check if the new file exists

mv hello.cpp hello NewFolder/ // Move file1.txt file2.txt into NewFolder, which is in the current directory

cd NewFolder // Get into NewFolder
ls
rm hello // Remove hello.exe
g++ hello.cpp -o hello // Restore hello.exe

mv hello.cpp hello_rename.cpp // Rename the file by using "mv file.txt newfile.txt"
mv hello_rename.cpp hello.cpp // Restore it
cd .. //
ls
mv NewFolder NewFolder_2 // Rename the folder by using "mv folder new_folder"
mv NewFolder_2 NewFolder // Restore it

find -name "hello.cpp" // Must provide the correct file name
grep -r "hello.cpp"
grep -r "hel"
grep -r "hel" ./NewFolder_2

help help ls // Check the documentations
help help mv

Now let’s take a look at more commands of linux:

mv hello.cpp hello ../NewFolder // Move file1.txt file2.txt into NewFolder, which is in the parent directory
mv hello.cpp hello ~/NewFolder // Move file1.txt file2.txt into NewFolder, which is in the home directory

rm FileName
rm *
rm -r *
rm -rf *

rm -r my_folder //
rm -rf my_folder //

(6) Introduction to Makefile

Refer to the article below for concise example code of Makefile.

C Interview Questions 02: Makefile, Scope and Lifetime, Call by Value, & Data Type

(7) Hands-On TCP Sockets

7–0 Result

7–1 Makefile of server.cpp

server: server.cpp
g++ -o server server.cpp

clean:
rm -f server

7–2 server.cpp


#include <iostream> // Include for input/output stream
#include <sys/socket.h> // Include for socket APIs
#include <netinet/in.h> // Include for internet protocols
#include <cstring> // Include for string operations
#include <unistd.h> // Include for POSIX operating system API

int main() {
int server_fd, new_socket; // Declare file descriptors for server and new socket
struct sockaddr_in address; // Structure for storing internet address
int opt = 1; // Option value for setsockopt
int addrlen = sizeof(address); // Length of the address structure

// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed"); // Print error if socket creation fails
exit(EXIT_FAILURE); // Exit with failure status
}

// Forcefully attaching socket to the port 8080
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt"); // Print error if setting socket options fails
exit(EXIT_FAILURE); // Exit with failure status
}
address.sin_family = AF_INET; // Set address family to AF_INET (IPv4)
address.sin_addr.s_addr = INADDR_ANY; // Accept connections from any IP
address.sin_port = htons(8080); // Set port to 8080 with proper byte order

// Bind the socket to the network address and port
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0) {
perror("bind failed"); // Print error if bind fails
exit(EXIT_FAILURE); // Exit with failure status
}

// Listen for incoming connections
if (listen(server_fd, 3) < 0) {
perror("listen"); // Print error if listen fails
exit(EXIT_FAILURE); // Exit with failure status
}

std::cout << "Listening..." << std::endl; // Print message indicating server is listening

// Accept an incoming connection
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
perror("accept"); // Print error if accept fails
exit(EXIT_FAILURE); // Exit with failure status
}

// Receive data from the client
char buffer[1024] = {0}; // Buffer to store data from client
int valread = read(new_socket, buffer, 1024); // Read data into buffer
std::cout << "Received message: " << buffer << std::endl; // Print received message

// Close the socket when done
close(server_fd); // Close the server file descriptor

return 0;
}

7–3 client.py

/*
Compile using "g++ -o client client.cpp -lws2_32"
*/

#include <iostream> // Include for input/output stream
#include <winsock2.h> // Include for Windows socket programming
#include <ws2tcpip.h> // Include for Windows socket programming

#pragma comment(lib, "Ws2_32.lib") // Link with Ws2_32.lib for socket functions

int main() {
WSADATA wsaData; // Structure to hold Winsock data
SOCKET sock = INVALID_SOCKET; // Declare a SOCKET variable
struct sockaddr_in serv_addr; // Structure for storing server address
const char* hello = "Hello from client"; // Message to send

// Initialize Winsock
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
std::cout << "Winsock initialization failed.\n"; // Print error if Winsock initialization fails
return 1;
}

// Create a socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
std::cout << "Socket creation failed with error: " << WSAGetLastError() << std::endl; // Print error if socket creation fails
WSACleanup(); // Clean up Winsock
return 1;
}

serv_addr.sin_family = AF_INET; // Set address family to IPv4
serv_addr.sin_port = htons(8080); // Set port to 8080 with proper byte order

// Set IP address of the server
serv_addr.sin_addr.s_addr = inet_addr("XXX.XXX.XX.XX"); // Replace with server IP address

// Check if IP address is valid
if (serv_addr.sin_addr.s_addr == INADDR_NONE) {
std::cout << "Invalid address/ Address not supported.\n"; // Print error if address is invalid
closesocket(sock); // Close the socket
WSACleanup(); // Clean up Winsock
return 1;
}

// Connect to the server
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
std::cout << "Connection Failed with error: " << WSAGetLastError() << std::endl; // Print error if connection fails
closesocket(sock); // Close the socket
WSACleanup(); // Clean up Winsock
return 1;
}

// Send a message to the server
send(sock, hello, strlen(hello), 0);
std::cout << "Hello message sent\n"; // Print confirmation of message sent

// Close the socket
closesocket(sock); // Close the socket
WSACleanup(); // Clean up Winsock

return 0;
}

Check the code with detailed comments on GitHub: Link to Repository

7–4 Commands

[A] Server

root@DemoYuChengKuo:~/SocketDemo# make 
g++ -o server server.cpp
root@DemoYuChengKuo:~/SocketDemo# ls
hello hello.cpp Makefile server server.cpp
root@DemoYuChengKuo:~/SocketDemo# ./server
Listening...

[B] Client

PS D:\Desktop\GitHub\tcp-sockets-cpp-ubuntu\clientCode> g++ -o client client.cpp -lws2_32
PS D:\Desktop\GitHub\tcp-sockets-cpp-ubuntu\clientCode> ./client
Hello message sent

(8) Understanding TCP/IP: Foundations and Theories

All of the screenshots above are from the online course The Bits and Bytes of Computer Networking on Coursera delivered by Google. You can grab a bird’s eye view of TCP/IP quickly from them.

8–1 | Introduction to Networking

Figures:
Figures:

8–2 | The Network Layer

Figures:
Figures:

8–3 | The Transport and Application Layers

Figures:
Figures:
Figures:
Figures:
Figures:

8–4 | Networking Servies

Figures:
Figures:
Figures:

--

--

Yu-Cheng (Morton) Kuo
Nerd For Tech

CS/DS blog with C/C++/Embedded Systems/Python. Embedded Software Engineer. Email: yc.kuo.28@gmail.com