/*! * 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 = 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: "); do { scanf("%s",buffer); send(client, buffer, bufsize, 0); if (buffer[0] == '#') { isExit = true; } } while (buffer[0] != '*' && buffer[0] != '#'); } 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; }