193 lines
9.2 KiB
C
193 lines
9.2 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 = 8000;//номера порта, на котором происходит соединение (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");
|
|||
|
//.....
|
|||
|
|
|||
|
/*
|
|||
|
Примечание: мы перейдем в этот момент только после того, как
|
|||
|
клиент успешно подключится к нашему серверу.
|
|||
|
Произойдет чтение сокета. Заметим, что read() будет
|
|||
|
блокировать до момента наличия чего-то для чтения
|
|||
|
Чтение будет происходить maximum 1024 символов в пакете
|
|||
|
*/
|
|||
|
//~ do {
|
|||
|
int result = recv(client, buffer, bufsize, 0);
|
|||
|
if (result < 0)
|
|||
|
{
|
|||
|
// ошибка получения данных
|
|||
|
printf("\n\n=> Connection terminated error %d with IP %s\n",result,inet_ntoa(server_addr.sin_addr));
|
|||
|
close(client);
|
|||
|
exit(1);
|
|||
|
}
|
|||
|
|
|||
|
char response[1024] = "HTTP/1.1 200 OK\r\n"
|
|||
|
"Version: HTTP/1.1\r\n"
|
|||
|
"Content-Type: text/html; charset=utf-8\r\n"
|
|||
|
"\r\n\r\n"
|
|||
|
"<title>Test C++ HTTP Server</title>\n"
|
|||
|
"<h1>Test page</h1>\n"
|
|||
|
"<p>This is body of the test page...</p>\n"
|
|||
|
"<h2>Request headers</h2>\n"
|
|||
|
"<em><small>Test C++ Http Server</small></em>\n";
|
|||
|
|
|||
|
|
|||
|
send(client, response,strlen(response), 0);
|
|||
|
}
|
|||
|
printf("\n\n=> Connection terminated with IP %s\n",inet_ntoa(server_addr.sin_addr));
|
|||
|
close(client);
|
|||
|
close(server);
|
|||
|
printf("\nGoodbye...");
|
|||
|
isExit = false;
|
|||
|
return 0;
|
|||
|
}
|