254 lines
11 KiB
C
254 lines
11 KiB
C
|
/*!
|
|||
|
* 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);
|
|||
|
|
|||
|
|
|||
|
do {
|
|||
|
char c;
|
|||
|
|
|||
|
printf("Client: ");
|
|||
|
|
|||
|
do
|
|||
|
{
|
|||
|
recv(client, &c, 1, 0);
|
|||
|
} while (c != 0x7E); //SOP
|
|||
|
|
|||
|
recv(client, &c, 1, 0);
|
|||
|
while (c != 0x7E) //EOP
|
|||
|
{
|
|||
|
if(c == 0x7D)
|
|||
|
{
|
|||
|
recv(client, &c, 1, 0);
|
|||
|
if(c == 0x5E)
|
|||
|
{
|
|||
|
c = 0x7E;
|
|||
|
}
|
|||
|
else if(c == 0x5D)
|
|||
|
{
|
|||
|
c = 0x7D;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("%c", 0x7D);
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
printf("%c", c);
|
|||
|
if (c == '#')
|
|||
|
{
|
|||
|
isExit = true;
|
|||
|
}
|
|||
|
recv(client, &c, 1, 0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
printf("Server: ");
|
|||
|
|
|||
|
c = 0x7E;
|
|||
|
send(client, &c, 1, 0); //SOP
|
|||
|
do
|
|||
|
{
|
|||
|
c = getchar();
|
|||
|
if(c == 0x7E)
|
|||
|
{
|
|||
|
c = 0x7D; send(client, &c, 1, 0);
|
|||
|
c = 0x5E; send(client, &c, 1, 0);
|
|||
|
continue;
|
|||
|
}
|
|||
|
if(c == 0x7D)
|
|||
|
{
|
|||
|
c = 0x7D; send(client, &c, 1, 0);
|
|||
|
c = 0x5D; send(client, &c, 1, 0);
|
|||
|
continue;
|
|||
|
}
|
|||
|
send(client, &c, 1, 0);
|
|||
|
if(c == '#')
|
|||
|
{
|
|||
|
isExit = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
} while (c != '\n');
|
|||
|
c = 0x7E;
|
|||
|
send(client, &c, 1, 0); //EOP
|
|||
|
|
|||
|
|
|||
|
} 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;
|
|||
|
}
|