Implementing Network Programming in C: TCP/IP Protocol
In today’s era of the internet, network programming has become an increasingly important skill. The C language, as a crucial tool for system-level programming, is widely used to implement various network applications. In this article, we will explore how to use C for network programming with the TCP/IP protocol, and provide code examples to help beginners understand.
What is TCP/IP?
TCP/IP (Transmission Control Protocol/Internet Protocol) is a set of communication protocols used to connect computers on different networks. It provides a standard way for data transmission over the internet or other computer networks, where:
- TCP: Provides reliable, connection-oriented data transmission.
- IP: Responsible for delivering packets from the source address to the destination address.
We will demonstrate how to use C for TCP/IP programming by building a simple client and server program.
Environment Setup
Ensure that your development environment has the following components installed:
- An IDE or text editor that supports C development (such as Visual Studio, Code::Blocks, Vim, etc.).
- The GCC compiler or any other software that supports C language compilation.
Creating the Server Side
First, we need to create a simple server that will listen for requests from clients on a specified port.
Here is a basic server code example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// Create socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// Set socket options
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt");
exit(EXIT_FAILURE);
}
// Define address structure
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);
// Bind socket to port
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// Start listening for connections
if (listen(server_fd, 3) < 0) {
perror("listen");
exit(EXIT_FAILURE);
}
printf("Waiting for client connection...\n");
while(1) {
// Accept new connection requests
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
read(new_socket, buffer, BUFFER_SIZE);
printf("Message: %s\n", buffer);
const char *hello = "Hello from server";
send(new_socket, hello, strlen(hello), 0);
close(new_socket);
memset(buffer, 0, sizeof(buffer));
}
return 0;
}
Program Explanation
-
Creating a Socket (
<span>socket</span>
): We first define a socket of type IPv4 (<span>AF_INET</span>
) and connection-oriented (<span>SOCK_STREAM</span>
). -
Setting Options (
<span>setsockopt</span>
): Set options to allow reuse of local addresses and ports, so that the service can restart without failing due to “Address already in use” errors. -
Binding the Socket (
<span>bind</span>
): Bind the previously created socket to the IP and PORT, letting the operating system know to listen for client requests on that IP and PORT. -
Starting to Listen (
<span>listen</span>
): The service enters a listening state, allowing a maximum of 3 connections in the queue (this can be adjusted as needed). -
Accepting Client Connections and Sending Messages (
<span>accept</span>
,<span>send</span>
,<span>read</span>
):
<span>accept()</span>
function will block the process until a client connection comes in.<span>read()</span>
reads the data sent by the client.- Then it responds with a message to the client and closes the current connection.
Creating the Client
Now, let’s write a simple TCP client that will connect to the server, send a message, and wait for a response.
Here is a basic client code example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
int main() {
int sock = 0;
struct sockaddr_in serv_addr;
char *hello = "Hello from client";
char buffer[1024] = {0};
// Create socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
// Convert IPv4 address from text to binary form
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
printf("\nInvalid address / Address not supported \n");
return -1;
}
// Try to connect to the server
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
printf("\nConnection Failed \n");
return -1;
}
send(sock, hello, strlen(hello), 0);
printf("Hello message sent\n");
read(sock, buffer, sizeof(buffer));
printf("%s\n", buffer);
close(sock);
return 0;
}
Program Explanation
- Creating a Socket, Setting Server Address and Port
- Using
<span>inet_pton()</span>
function to convert the string format IP address to actual format. - Using
<span>connect()</span>
function to attempt to connect to the service program running on the same IP and PORT; if it fails, it outputs a “Connecting failed” message. - Send a message to the service and read the returned information, then close the socket.
Compilation and Running Steps
Steps on Linux platform are as follows:
Compile Server and Client:
gcc server.c -o server
gcc client.c -o client
Start Server:
./server &
At this point, the server will run in the background to accept requests from the client.
Start Client:
./client
You should be able to see the message sent from the client to the server and the response returned from the server.
Notes:
- Ensure the SERVER is started before starting the CLIENT.
- If you need to test multiple times, make sure to clear the Data Buffer after each run to avoid reading old content that may affect the results.
Conclusion
This article introduced some basic network programming techniques using C language based on the TCP/IP protocol, including establishing a server and its corresponding communication relationships. Although DDoS attacks have limitations, they can be expanded into robust and flexible foundations. They can further develop into complex frameworks choosing appropriate asynchronous or multithreaded structures, etc. This is just the beginning, and I hope it inspires you to delve deeper into the exploration of the digital world!
Feel free to raise more questions on this topic.