Начинающий
-
Автор темы
- #1
Всем Привет, в общем, делаю курсовую, почти доделал в принципе остался последний момент.
При отключении одного из клиентов от сервера у остальных начинает спамить в консоли последнее сообщение.
я сделал пример, но он не очень корректно работает
if (recv(Connections[index], msg, sizeof(msg), NULL) > 0) и дальше, находится строчка в ProcessPacket
При отключении одного из клиентов от сервера у остальных начинает спамить в консоли последнее сообщение.
я сделал пример, но он не очень корректно работает
if (recv(Connections[index], msg, sizeof(msg), NULL) > 0) и дальше, находится строчка в ProcessPacket
C++:
#include <iostream>// Стандартная библиотека для работы большей части кода, так же там есть ввод и вывод, как раз то что нам нужно!
//#include <Windows.h> // Windows. h нужен для того, что бы ты мог использовать в своей программе функционал, предоставляемый операционной системой (Windows 95, 98, NT, 2000, XP).
//#include <TlHelp32.h> // Данная библиотека нужна для того что бы мы могли работать с разными процессами.
#include <conio.h>
#include <winsock2.h>// Для роботы с сетью в Windows есть специальная библиотека Win Сокет, есть 2 версии данной библиотеки: 1 и 2, я буду использовать 2-ю, так как она более свежая и там больше функций для работы с сетью.
#pragma comment(lib, "ws2_32.lib") // без pragma comment'а мы не сможем получить доступ к некоторым функциям.
#include <thread>
#include <WS2tcpip.h>
#include <vector>
#pragma warning(disable: 4996)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
SOCKET Connections[100];
int Counter = 0;
enum Packet {
P_ChatMessage,
P_Test
};
class Color
{
public:
Color(int desiredColor) {
consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
color = desiredColor;
}
friend ostream& operator<<(ostream& ss, Color obj) {
SetConsoleTextAttribute(obj.consoleHandle, obj.color);
return ss;
}
private:
int color;
HANDLE consoleHandle;
/*
0 = чёрный
1 = синий
2 = зелёный
3 = светло-синий
4 = красный
5 = фиолетовый
6 = золотой
7 = белый
*/
};
bool ProcessPacket(int index, Packet packettype) {
switch (packettype) {
case P_ChatMessage:
{
int msg_size;
recv(Connections[index], (char*)&msg_size, sizeof(int), NULL);
char* msg = new char[msg_size + 1];
msg[msg_size] = '\0';
recv(Connections[index], msg, msg_size, NULL);
//if (recv(Connections[index], msg, sizeof(msg), NULL) > 0) {
for (int i = 0; i < Counter; i++) {
if (i == index || Connections[i] == INVALID_SOCKET) {
continue;
}
Packet msgtype = P_ChatMessage;
send(Connections[i], (char*)&msgtype, sizeof(Packet), NULL);
send(Connections[i], (char*)&msg_size, sizeof(int), NULL);
send(Connections[i], msg, msg_size, NULL);
}
//}
/*else {
::closesocket(Connections[index]);
Connections[index] = INVALID_SOCKET;
return 1;
}*/
delete[] msg;
break;
}
default:
cout << "Unrecognized packet: " << packettype << endl;
break;
}
return true;
}
void ClientHandler(int index) {
Packet packettype;
while (true) {
recv(Connections[index], (char*)&packettype, sizeof(Packet), NULL);
if (!ProcessPacket(index, packettype)) {
break;
}
//std::cout << packettype;
}
closesocket(Connections[index]);
}
void WinSoket()
{
cout << "Wait on the Client(s)..." << endl;
// Прежде чем начать работу с сетью, нужно загрузить необходимую версию библиотеки. Если этого не сделать то любой пользователь в сетевой функции вернёт ошибку.
// Для загрузки библиотеки используется функция WSAStartUp. Но для начала нам необходимо создать структуру WSAData
WSAData wsaData; // Затем создаем переменную WORD
WORD DLLVersion = MAKEWORD(2, 1); // Это запрашиваемая версия библиотеки WinSoc, она нам еще понадобиться для того что бы загрузить библиотеку.
// И так давайте по подробнее про функцию WSAStartUp.
//Первым параметром передается Запрашиваемая версия Библиотеки. Вторым параметром сюда передается ссылка на структуру WSAData.
// Сразу же после попытки загрузить библиотеку нужно сделать проверку. Если библиотека загрузилась удачно, то она вернет значение 0.
// Теперь нам нужно написать проверку Если WSAStartUP не вернет 0 то мы просто выйдем из функции main и выведем ошибку, о том что библиотека не загрузилась.
if (WSAStartup(DLLVersion, &wsaData) != 0) {
cout << "Error!!" << endl;
Sleep(1000);
exit(1);
}// И так мы записали функцию библиотеки и проверили ее, вернет ли она ошибку, если да, то мы выходим из функции.
// После загрузки библиотеки, необходимо заполнить информацию об адрессе Сокета.
SOCKADDR_IN addr; // Структура SOCKADDR предназначена для хранение адресса. Для Имя протоколов используется имя SOCKADDR_IN, я назвал ее addr.
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // sin_addr это структура SOCKADDR_IN, которая хранит ip address, я указал здесь localhost.
addr.sin_port = htons(1111); // sin_port - порт для идентификации программы с поступающими данными, я выбрал порт 1111, можно указать любой порт, главное что бы он не был зарезервирован другой программой.
addr.sin_family = AF_INET; // sin_family - семейство протоколов. Для интернет протоколов указывается константа AF_INET.
// Что бы 2 комп'ютера смогли установить соединение, один из них должен запустить прослушивание на опредёленном порту.
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL); // И так, я создал сокет и назвал его sListen, которому присвоил результат на выполнение функции сокета, который передал 3 параметра.
// Первый параметр AF_INET, оначает что будет использоваться семейство интернет протоколов
// Второй параметр SOCK_STREAM указывает на протокол устанавливающий соединение.
// Третий параметр нам пока не нужен, по этому я указал NULL.
// Теперь нам нужно привязать аддресс Сокета, для привязки аддресса сокета используется спец. функция, называется она bind, пишеться след. образом
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));// Так, что за параметры я сюда передал?
// Первый параметр это предварительно созданный сокет, в моем случаи это sListen
// Второй параметр это указатель на структуру SOCK_ADDR_IN
// Третий параметр это размер структуры SOCK_ADDR_IN
// После того как локальный аддресс и порт привязаны к сокету, нужно приступить к прослушиванию порта в ожидании соединения со стороны клиента.
// Для этого служит функция Listen которая выглядит след. образом.
listen(sListen, SOMAXCONN); // И так, какие я параметры сюда передал:
// Первый параметр этто все тот же сокет который был создан и к которому был привязан адресс.
// По этим данным функция определит по какому порту нужно запустить прослушивание
// Второй параметр это максимально допустимое число запросов ожидающих обработки.
// Допустим, что я указал здесь значение 3,а мне пришло 5 запросов на соединение от разных клиентов. Только 3 из них станут в очередь, а остальные получат ошибку.
// Теперь мы фактически принимаем это соединение, по этому я собираюсь запустить новый сокет что бы удерживать соединение с клиентом.
SOCKET newConnection;
for (int i = 0; i < 100; i++)
{
newConnection = accept(sListen, (SOCKADDR*)&addr, &sizeofaddr);
// И так я создал новый сокет, назвал его newConnection, присвоил ему результат ввыполнения функции accept в которую передал 3 параметра.
// Первый параметр это только что созданый и запущенный на прослушивание сокет.
// Второй параметр это указатель на структуру типа SOCKADDR.
// Третий параметр это размер на структуры SOCKADDR. Но так как у меня в 3 параметре выбивает ошибку, мне придеться создать новую переменную и присвоить туда размер структуры SOCKADDR, после указать в качестве 3-го параметра функции accept, ссылку на эту переменную.
// После выполнения функции accept, 2-й параметр addr будет держать сведения об ип адресе клиента, который призвал подключения, эти данные можно использовать для контроля доступа к серверу по ip-адресу.
// Функция accept возвращает указатель на новый сокет, который можно использовать для общения с клиентом. Если функция 0-е значение, значит клиент не сможет подключится к серверу.
// на этот случай нам нужно написать проверку
if (newConnection == 0) { // Если newConnection не будет равен 0, то мы удачно установили соединение с пользователем, выведем в консоль сообщение, что пользователь присоединился.
cout << "Error #2" << endl;
}
else {
cout << Color(7) << "Client " << Color(4) << getenv("USERNAME") << Color(7) << " Connected!" << endl;
string msg = " Test message automatically sent";
int msg_size = msg.size();
Packet msgtype = P_ChatMessage;
send(newConnection, (char*)&msgtype, sizeof(Packet), NULL);
send(newConnection, (char*)&msg_size, sizeof(int), NULL);
send(newConnection, msg.c_str(), msg_size, NULL);
Connections[i] = newConnection;
Counter++;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandler, (LPVOID)(i), NULL, NULL);
Packet testpacket = P_Test;
send(newConnection, (char*)&testpacket, sizeof(Packet), NULL);
}
}
}
int main() {
WinSoket();
system("pause");
return 0;
}