MIPI_AdvancedC_FRTK/Seminar1/43_server_1.c
2024-11-14 08:45:50 +03:00

230 lines
10 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* Simple chat program (server side).cpp - http://github.com/hassanyf
* Version - 2.0.1
*
* Copyright (c) 2016 Hassan M. Yousuf
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
/* ---------- ИНИЦИАЛИЗАЦИЯ ПЕРЕМЕННЫХ СЕРВЕРА---------- */
/*
1. client/server - два файл-дескриптора.
Эти две перемнные хранят значение сокетов,
которые вернула система при подключении.
2. portNum нужен для хранения номера порта, на котором происходит соединение.
3. isExit - булевая переменная, признак завершения программы.
4. Сервер считывает сообщение из сокет соединения в буффер обмена для приема/отправки данных к/от сервера.
*/
int server;//файл-дескриптор сервера
int client;//файл-дескриптор клиента
int portNum = 1500;//номера порта, на котором происходит соединение (0 до 65535)
bool isExit = false;//булевая переменная, признак завершения программы.
int bufsize = 1024;//размер буффера
char buffer[bufsize]; //буффер обмена для приема/отправки данных к/от сервера
/*
5. A sockaddr_in - структура, содержащая интернет адрес, с которым будет установлено соединение.
Эта структура уже определена в netinet/in.h, поэтому нет необходимости
заново ее задавать.
DEFINITION:
struct sockaddr_in
{
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
6. serv_addr будет содержать адрес сервера
7. socklen_t - длиной по крайней мере 32 бита
*/
struct sockaddr_in server_addr;
socklen_t size;
/* ---------- УСТАНОВКА СОКЕТ СОЕДИНЕНИЯ ----------*/
/* --------------- socket() функция ------------------*/
/*
Socket() функция создает новый сокет.
На вход получает 3 аргумента,
a. AF_INET: доменный адрес сокета.
b. SOCK_STREAM: тип сокета. Потоковый сокет, в котором сообщение
читается последовательным потоком(TCP).
c. Третий это аргумент протокола: всегда должен быть 0.
Операционная система выберет самый подходящий протокол.
Функция возвращает файл-дескриптор целое число - соединение, которое используется для всех ссылок
на этот сокет. Если произошла ошибка соединения, то возвращается -1.
*/
server = socket(AF_INET, SOCK_STREAM, 0);
printf("SERVER\n");
if (server < 0)
{
printf("Error establishing socket...\n");
exit(1);
}
printf("=> Socket server has been created...\n");
/*
Переменная serv_addr - структура sockaddr_in.
sin_family содержит код для адресной семьи.
Всегда должна быть установлена AF_INET.
INADDR_ANY содержит IP-адрес хоста. Для сервера - это
IP-адрес компьютера, на котором работает сервер.
htons() функция переводит номер порта из порядка байтов хоста
в номер порта в порядке байтов сети.
*/
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htons(INADDR_ANY);
server_addr.sin_port = htons(portNum);
/* ---------- ПРИВЯЗКА СОКЕТА ---------- */
/* ---------------- bind() ---------------- */
/*
bind() функция привязывает сокет к адресу, то есть в нашем случае
к адресу сервера и номеру порта, на котором сервер будет работать.
Для этого нужны три аргумента: файл дескриптор сокета, указатель на структуру
типа sockaddr (должен указывать на правильный тип)
*
*/
if ((bind(server, (struct sockaddr*)&server_addr,sizeof(server_addr))) < 0)
{
printf("=> Error binding connection, the socket has already been established...\n");
return -1;
}
size = sizeof(server_addr);
printf("=> Looking for clients...\n");
/* ------------- ПРОСЛУШИВАНИЕ СОКЕТА ------------- */
/* ---------------- listen() ---------------- */
/*
Функция listen позволяет прослушивать сокета для подключения.
Программа будет в состоянии простоя, если подключений не обнаружится.
Первый аргумент - это файл дескриптор сокета, второй - количество клиентов,
то есть количество подключений, которое сервер может обработать, пока процесс
обрабатывает определенное подключение. Максимальное число клиентов во многих системах равняется 5.
*/
listen(server, 1);
/* ------------- ПОДКЛЮЧЕНИЕ КЛИЕНТОВ ------------- */
/* ----------------- accept() ------------------- */
/*
Система accept() вызывает процесс блокировки пока клиент подключается к серверу.
К тому же, она пробуждает процесс, когда подключение с клиентом было успешно установлено.
Она возвращает новый файл дескриптор, и вся связь по этому подключению должна
осуществляться, используя новый файл дескриптор. Второй аргумент - указатель на
адрес клиента, третий - размер структуры.
*/
int clientCount = 1;
client = accept(server,(struct sockaddr *)&server_addr,&size);
// первая проверка действительности подключения
if (client < 0)
printf("=> Error on accepting...");
//Основной цикл
while (client > 0)
{
printf("=> Connected with the client %d, you are good to go...\n",clientCount);
printf("\n=> Enter # to end the connection\n");
//.....
strcpy(buffer, "=> Server connected...\n");
send(client, buffer, bufsize, 0);
/*
Примечание: мы перейдем в этот момент только после того, как
клиент успешно подключится к нашему серверу.
Произойдет чтение сокета. Заметим, что read() будет
блокировать до момента наличия чего-то для чтения
Чтение будет происходить maximum 1024 символов в пакете
*/
do {
printf("Client: ");
do {
recv(client, buffer, bufsize, 0);
printf("%s ",buffer);
if (buffer[0] == '#')
{
isExit = true;
}
} while (buffer[0] != '*' && buffer[0] != '#');
printf("\nServer: ");
#if 0
do {
scanf("%s",buffer);
send(client, buffer, bufsize, 0);
if (buffer[0] == '#')
{
isExit = true;
}
} while (buffer[0] != '*' && buffer[0] != '#');
#else
char c;
int cntr = 0;
while((c=getchar())!='\n')//till end of line
buffer[cntr++]=c;
buffer[cntr++] = 0;
if (buffer[0] == '#')
isExit = true;
send(client, buffer, bufsize, 0);
send(client, "*", 2, 0);
#endif
} while (!isExit);
/*
Как только соединение будет установлено, оба конца связи могут
писать и читать соединение. Все, написанное клиентом, будет прочитано сервером,
а все, написанное сервером, будет прочитано клиентом.
*/
/* ---------------- ЗАВЕРШЕНИЕ СОЕДИНЕНИЯ ------------- */
/* ----------------- close() --------------- */
/*
Как только сервер нажмет # для завершения соединения,
цикл завершится и закроет серверное сокет соединение
и клиентсткое соединение.
*/
// inet_ntoa переводит данные пакета в IP-адрес, который был взят у клиента
printf("\n\n=> Connection terminated with IP %s\n",inet_ntoa(server_addr.sin_addr));
close(client);
close(server);
printf("\nGoodbye...");
isExit = false;
//~ exit(1);
return 0;
}
return 0;
}