/*! * Simple chat program (server side).cpp - http://github.com/hassanyf * Version - 2.0.1 * * Copyright (c) 2016 Hassan M. Yousuf */ #include #include #include #include #include #include #include #include #include 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" "Test C++ HTTP Server\n" "

Test page

\n" "

This is body of the test page...

\n" "

Request headers

\n" "Test C++ Http Server\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; }