Linux run on socket

How to Code a Server and Client in C with Sockets on Linux – Code Examples

In a previous example we learnt about the basics of socket programming in C. In this example we shall build a basic ECHO client and server. The server/client shown here use TCP sockets or SOCK_STREAM.

Tcp sockets are connection oriented, means that they have a concept of independent connection on a certain port which one application can use at a time.

The concept of connection makes TCP a «reliable» stream such that if errors occur, they can be detected and compensated for by resending the failed packets.

Server

Lets build a very simple web server. The steps to make a webserver are as follows :

1. Create socket
2. Bind to address and port
3. Put in listening mode
4. Accept connections and process there after.

/* C socket server example */ #include #include //strlen #include #include //inet_addr #include //write int main(int argc , char *argv[]) < int socket_desc , client_sock , c , read_size; struct sockaddr_in server , client; char client_message[2000]; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) < printf("Could not create socket"); >puts("Socket created"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) < //print the error message perror("bind failed. Error"); return 1; >puts("bind done"); //Listen listen(socket_desc , 3); //Accept and incoming connection puts("Waiting for incoming connections. "); c = sizeof(struct sockaddr_in); //accept connection from an incoming client client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c); if (client_sock < 0) < perror("accept failed"); return 1; >puts("Connection accepted"); //Receive a message from client while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 ) < //Send the message back to client write(client_sock , client_message , strlen(client_message)); >if(read_size == 0) < puts("Client disconnected"); fflush(stdout); >else if(read_size == -1) < perror("recv failed"); >return 0; >

The above code example will start a server on localhost (127.0.0.1) port 8888
Once it receives a connection, it will read some input from the client and reply back with the same message.
To test the server run the server and then connect from another terminal using the telnet command like this

Client

Now instead of using the telnet program as a client, why not write our own client program. Quite simple again

/* C ECHO client example using sockets */ #include //printf #include //strlen #include //socket #include //inet_addr #include int main(int argc , char *argv[]) < int sock; struct sockaddr_in server; char message[1000] , server_reply[2000]; //Create socket sock = socket(AF_INET , SOCK_STREAM , 0); if (sock == -1) < printf("Could not create socket"); >puts("Socket created"); server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_family = AF_INET; server.sin_port = htons( 8888 ); //Connect to remote server if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) < perror("connect failed. Error"); return 1; >puts("Connected\n"); //keep communicating with server while(1) < printf("Enter message : "); scanf("%s" , message); //Send some data if( send(sock , message , strlen(message) , 0) < 0) < puts("Send failed"); return 1; >//Receive a reply from the server if( recv(sock , server_reply , 2000 , 0) < 0) < puts("recv failed"); break; >puts("Server reply :"); puts(server_reply); > close(sock); return 0; >

The above program will connect to localhost port 8888 and then ask for commands to send. Here is an example, how the output would look

$ gcc client.c && ./a.out Socket created Connected Enter message : hi Server reply : hi Enter message : how are you

Server to handle multiple connections

The server in the above example has a drawback. It can handle communication with only 1 client. Thats not very useful.

Читайте также:  Run history command in linux

One way to work around this is by using threads. A thread can be assigned for each connected client which will handle communication with the client.

/* C socket server example, handles multiple clients using threads */ #include #include //strlen #include //strlen #include #include //inet_addr #include //write #include //for threading , link with lpthread //the thread function void *connection_handler(void *); int main(int argc , char *argv[]) < int socket_desc , client_sock , c , *new_sock; struct sockaddr_in server , client; //Create socket socket_desc = socket(AF_INET , SOCK_STREAM , 0); if (socket_desc == -1) < printf("Could not create socket"); >puts("Socket created"); //Prepare the sockaddr_in structure server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons( 8888 ); //Bind if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0) < //print the error message perror("bind failed. Error"); return 1; >puts("bind done"); //Listen listen(socket_desc , 3); //Accept and incoming connection puts("Waiting for incoming connections. "); c = sizeof(struct sockaddr_in); //Accept and incoming connection puts("Waiting for incoming connections. "); c = sizeof(struct sockaddr_in); while( (client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c)) ) < puts("Connection accepted"); pthread_t sniffer_thread; new_sock = malloc(1); *new_sock = client_sock; if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) new_sock) < 0) < perror("could not create thread"); return 1; >//Now join the thread , so that we dont terminate before the thread //pthread_join( sniffer_thread , NULL); puts("Handler assigned"); > if (client_sock < 0) < perror("accept failed"); return 1; >return 0; > /* * This will handle connection for each client * */ void *connection_handler(void *socket_desc) < //Get the socket descriptor int sock = *(int*)socket_desc; int read_size; char *message , client_message[2000]; //Send some messages to the client message = "Greetings! I am your connection handler\n"; write(sock , message , strlen(message)); message = "Now type something and i shall repeat what you type \n"; write(sock , message , strlen(message)); //Receive a message from client while( (read_size = recv(sock , client_message , 2000 , 0)) >0 ) < //Send the message back to client write(sock , client_message , strlen(client_message)); >if(read_size == 0) < puts("Client disconnected"); fflush(stdout); >else if(read_size == -1) < perror("recv failed"); >//Free the socket pointer free(socket_desc); return 0; >

Run the above server and connect from multiple clients and it will handle all of them. There are other ways to handle multiple clients, like select, poll etc.

We shall talk about them in some other article. Till then practise the above code examples and enjoy.

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected] .

64 Comments

  1. Zamer Chaudhary April 16, 2022 at 2:02 pm Hey sir i need you help related to Develop a client/server application using Linux TCP sockets and the C programming language.
    I will share the more information on mail. Please respond me.
    [email protected]
  1. Alex June 8, 2017 at 4:04 pm Yes, it fixes the bug with memory leakage but disables the multiple clients functionality, so this example doesn’t really work 🙁 I cann’t solve this problem yet.
  1. David November 5, 2021 at 1:01 am I’m wondering that, too. As soon as I change from the localhost address, I start getting message refused on the client.
Читайте также:  Altlinux vs astra linux

Источник

Сокеты в ОС Linux

В данной статье будет рассмотрено понятие сокета в операционной системе Linux: основные структуры данных, как они работают и можно ли управлять состоянием сокета с помощью приложения. В качестве практики будут рассмотрены инструменты netcat и socat.

Что такое сокет?

Сокет — это абстракция сетевого взаимодействия в операционной системе Linux. Каждому сокету соответствует пара IP-адрес + номер порта. Это стандартное определение, к которому привыкли все, спасибо вики. Хотя нет, вот здесь лучше описано. Поскольку сокет является только лишь абстракцией, то связка IP-адрес + номер порта — это уже имплементация в ОС. Верное название этой имплементации — «Интернет сокет». Абстракция используется для того, чтобы операционная система могла работать с любым типом канала передачи данных. Именно поэтому в ОС Linux Интернет сокет — это дескриптор, с которым система работает как с файлом. Типов сокетов, конечно же, намного больше. В ядре ОС Linux сокеты представлены тремя основными структурами:

  1. struct socket — представление сокета BSD, того вида сокета, который стал основой для современных «Интернет сокетов»;
  2. struct sock — собственная оболочка, которая в Linux называется «INET socket»;
  3. struct sk_buff — «хранилище» данных, которые передает или получает сокет;

Как видно по исходным кодам, все структуры достаточно объемны. Работа с ними возможна при использовании языка программирования или специальных оберток и написания приложения. Для эффективного управления этими структурами нужно знать, какие типы операций над сокетами существуют и когда их применять. Для сокетов существует набор стандартных действий:

  • socket — создание сокета;
  • bind — действие используется на стороне сервера. В стандартных терминах — это открытие порта на прослушивание, используя указанный интерфейс;
  • listen — используется для перевода сокета в прослушивающее состояние. Применяется к серверному сокету;
  • connect — используется для инициализации соединения;
  • accept — используется сервером, создает новое соединение для клиента;
  • send/recv — используется для работы с отправкой/приемом данных;
  • close — разрыв соединения, уничтожение сокета.

Если о структурах, которые описаны выше, заботится ядро операционной системы, то в случае команд по управлению соединением ответственность берет на себя приложение, которое хочет пересылать данные по сети. Попробуем использовать знания о сокетах для работы с приложениями netcat и socat.

netcat

Оригинальная утилита появилась 25 лет назад, больше не поддерживается. На cегодняшний день существуют порты, которые поддерживаются различными дистрибутивами: Debian, Ubuntu, FreeBSD, MacOS. В операционной системе утилиту можно вызвать с помощью команды nc, nc.traditional или ncat в зависимости от ОС. Утилита позволяет «из коробки» работать с сокетами, которые используют в качестве транспорта TCP и UDP протоколы. Примеры сценариев использования, которые, по мнению автора, наиболее интересны:

  • перенаправление входящих/исходящих запросов;
  • трансляция данных на экран в шестнадцатеричном формате.

Опробуем операции в действии. Задача будет состоять в том, что необходимо отправить TCP данные через netcat в UDP соединение. Для лабораторной будет использоваться следующая топология сети:

  1. Введем команду на открытие порта на машине Destination: nc -ulvvp 7878
  2. Настроим машину Repeater. Так как передача из одного интерфейса этой машины будет происходить по протоколу TCP, а на другой интерфейс будет осуществляться передача по протоколу UDP, то для таких действий необходимо сделать соединитель, который сможет накапливать данные и пересылать их между открытыми портами. На такую роль отлично подходит FIFO файл. Поэтому команда для запуска будет выглядеть так: sudo mkfifo /tmp/repeater #создать FIFO файл
    sudo nc -l -p 4545 > /tmp/repeater | nc -u 10.0.3.5 7878 < /tmp/repeater IP адрес 10.0.3.5 - адрес машины Destination. Символы "|" и ">
  3. Запускаем соединение из машины Source: nc 10.0.2.4 4545

В итоге получаем возможность читать данные от машины Source:

Пример с трансляцией данных в шестнадцатеричном формате можно провести так же, но заменить команду на Destination или добавить еще один пайп на Repeater:

В результате будет создан файл, в котором можно будет обнаружить передаваемые данные в шестнадцатеричном формате:

Как видно из тестового сценария использования, netcat не дает контролировать практически ничего, кроме направления данных. Нет ни разграничения доступа к ресурсам, которые пересылаются, ни возможности без дополнительных ухищрений работать с двумя сокетами, ни возможности контролировать действия сокета. Протестируем socat.

socat

Инструмент, который до сих пор поддерживается и имеет весьма обширный функционал по склейке каналов для взаимодействия. Разработчиками инструмент именуется как netcat++. Ниже приведем небольшой список того что можно перенаправить через socat:

  • STDIO -> TCP Socket;
  • FILE -> TCP Socket;
  • TCP Socket -> Custom Application;
  • UDP Socket -> Custom Application;
  • Socket -> Socket.

Для повседневного использования достаточно опций, но если понадобится когда-то работать напрямую с серийным портом или виртуальным терминалом, то socat тоже умеет это делать. Полный перечень опций можно вызвать с помощью команды:

Помимо редиректов socat также можно использовать как универсальный сервер для расшаривания ресурсов, через него можно как через chroot ограничивать привилегии и доступ к директориям системы.

Чтобы комфортно пользоваться этим инструментом, нужно запомнить шаблон командной строки, который ожидает socat:

socat additionalOptions addr1 addr2

  • additionalOptions — опции, которые могут добавлять возможности логирования информации, управления направлением передачи данных;
  • addr1 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;
  • addr2 — источник данных или приемник (влияет использование флага U или u), это может быть сокет, файл, пайп или виртуальный терминал;

Попробуем провести трансляцию данных из сокета в сокет. Будем использовать для этого 1 машину. Перед началом эксперимента стоит отметить, что особенностью socat является то, что для его корректной работы нужно обязательно писать 2 адреса. Причем адрес не обязательно должен быть адресом, это может быть и приложение, и стандартный вывод на экран.

Например, чтобы использовать socat как netcat в качестве TCP сервера, можно запустить вот такую команду:

socat TCP-LISTEN:4545, STDOUT

Для коннекта можно использовать netcat:

При таком использовании, socat дает возможность пересылать сообщения в обе стороны, но если добавить флаг «-u», то общение будет только от клиента к серверу. Все серверные сообшения пересылаться не будут:

Настроим более тонко наш сервер, добавив новые опции через запятую после используемого действия:

socat TCP-LISTEN:4545,reuseaddr,keepalive,fork STDOUT

Дополнительные параметры распространяются на те действия, которые socat может выполнять по отношению к адресу. Полный список опций можно найти здесь в разделе «SOCKET option group».

Таким образом socat дает практически полный контроль над состоянием сокетов и расшариваемых ресурсов.

Статья написана в преддверии старта курса Network engineer. Basic. Всех, кто желает подробнее узнать о курсе и карьерных перспективах, приглашаем записаться на день открытых дверей, который пройдет уже 4 февраля.

Источник

Оцените статью
Adblock
detector