Чтобы начать пилить этот чат, нам понадобится:
Проект будем реализовывать на основе двух разных проектах – первая часть сервак, вторая часть клиент. Все функции и детальный разбора я делать не буду, для этого нет времени, за то есть доп. ссылки и книги, которые я прикреплю. Для пущего понимая, я полагаю вы знакомы, что такое TCP/IP, ООП, функции и их вызов, структуры (С++).
Литература
UNIX сетевые?
C++ сетевое?
TCP/IP стек?
И так поехали
Для начала мы подключаем препроцессорную директиву «WS2tcpip.h» для работы с winsocks. Самая главная функция для реализации winsocks это WSAStartup(принимает два параметра 1 – Версия, которая будет использоваться, 2 – указатель на адрес памяти структуры WSADATA). Остальные коменты написаны сразу в коде.
Написали, проверили код, делаем компиляцию
Теперь включаем Putty
Делаем main() func для клиента
Теперь добавляем для удобства запуска серверную часть
Проверяем, правильно ли добавили все файлы. Теперь выбираем свойства нашего решения
И теперь запускаете F5 и ловите пруфы от своего чата.
- Желание
- VS-2019 (студия)
- WS2tcpip.h (библиотека для winsocks, качать не надо, она уже «вмонтирована»)
- Прямые руки
- Программа Putty Скачать
- Свободного времени 1 час (с перерывами на сижку)
- Кофе (по желанию Пиво)
- Защита от пагубного влияния ретроградного Меркурия, если не помогло – см. пункт 1
Проект будем реализовывать на основе двух разных проектах – первая часть сервак, вторая часть клиент. Все функции и детальный разбора я делать не буду, для этого нет времени, за то есть доп. ссылки и книги, которые я прикреплю. Для пущего понимая, я полагаю вы знакомы, что такое TCP/IP, ООП, функции и их вызов, структуры (С++).
Литература
UNIX сетевые?
C++ сетевое?
TCP/IP стек?
И так поехали
Часть 1 - Сервер
Для начала мы подключаем препроцессорную директиву «WS2tcpip.h» для работы с winsocks. Самая главная функция для реализации winsocks это WSAStartup(принимает два параметра 1 – Версия, которая будет использоваться, 2 – указатель на адрес памяти структуры WSADATA). Остальные коменты написаны сразу в коде.
C++:
#include <iostream>
#include <WS2tcpip.h> // сокеты windows API для доступа к сетевым сокетам, будем работать через них. К тому
// же он включает функции для запила winsock и много других фу-й, которые мы будем юзать в ходе разработки
#pragma comment(lib, "ws2_32.lib")
using namespace std; // да-да знаю, bad practice, но учитывая что это дом. работа, упустим излишества
void main()
{
setlocale(LC_ALL, "Russian");
WSAData WinSockObj; // структура для создания winsocks
WORD version = MAKEWORD(2, 2); // версия winsocks, которые мы будем использовать
int winSock = WSAStartup(version, &WinSockObj); // передаем в ф-ю версию нашего винсокса и адрес самого obj winsocks
if (winSock != 0)
{
cerr << "Программа не может создать winsock!" << endl; // думаю по циклу ясно, а поток выбрали конечно соответственный для ошибок
return;
}
SOCKET listenSockServer = socket(AF_INET, SOCK_STREAM, 0); // версия inet - IPV4 , используем поток сокет, а последнее значение просто ноль.
if (listenSockServer == INVALID_SOCKET) // +- тоже самое, что в предыдущем ветвлении, только отличает второй операнд на сравнение
{
cerr << "Программа не может создать сокет!" << endl;
return;
}
sockaddr_in addressIPForSocket; // создаем на основе структуры объект на основе выбранного протокола
int serverSizeAddr = sizeof(addressIPForSocket);
addressIPForSocket.sin_family = AF_INET; // семейство inet - IPV4
addressIPForSocket.sin_port = htons(40000); // способ передачи байтов от хоста к сети, номер порта рандом - главное, чтобы он у вас бы не занят какой то службой
addressIPForSocket.sin_addr.S_un.S_addr = INADDR_ANY;
bind(listenSockServer, (sockaddr*)&addressIPForSocket, serverSizeAddr); // связываем наш сокет с портом, указатель на адрес и размер структуры адреса
listen(listenSockServer, SOMAXCONN); // начинаем чекать сокет на прослушку порта, SOMAXCONN максимальная длина очереди, которая прослушивает порт.
sockaddr_in addrClient; // начинаем ожидать первых подключений
int clientSizeAddr = sizeof(addrClient); // получаем размер адреса клиента
SOCKET clientSocket = accept(listenSockServer, (sockaddr*)&addrClient, &clientSizeAddr); // слушаем, получаем адрес, порт по которому клиент может приконектиться
if (clientSocket == INVALID_SOCKET)
{
cerr << "Программе не удалось подключить клиента по данному порту!" << endl;
}
char host[NI_MAXHOST]; // для получение имени клиента, который приконектился
char service[NI_MAXHOST]; // порт по которому клиент приконектился
ZeroMemory(host, NI_MAXHOST); // default заполняет память нулями, первый параметр это указатель на блок памяти, который нужно заполнить, а второй размер в байтах этой же памяти
ZeroMemory(service, NI_MAXSERV);
// getnameinfo это ф-ю которая преобра. адрес сокета в независимый хост\службу, притом похерестично на каком протоколе это базируется.
if (getnameinfo((sockaddr*)&clientSocket, clientSizeAddr, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) // одним словом, если коннект удался...
{
cout << host << " Удачно подключился по порту " << service << endl;
}
else
{
inet_ntop(AF_INET, &addrClient.sin_addr, host, NI_MAXHOST);
cout << host << " подключение по порту " << htons(addrClient.sin_port) << endl;
}
closesocket(listenSockServer);
char bufferSize[10000]; // размер буфера, делайте какой захотите сами
while (true) // ожидаем клиента, пока он не отправит какие либо данные
{
ZeroMemory(bufferSize, 10000);
int dataReceived = recv(clientSocket, bufferSize, 10000, 0); // ф-ю для отправки данных
if (dataReceived == SOCKET_ERROR)
{
cerr << "Программа словила ошибку в отправке данных" << endl;
break;
}
if (dataReceived == 0) // если ничего не получили, значить клиент вышел
{
cout << "Клиент покинул чат" << endl;
break;
}
cout << string(bufferSize, 0, dataReceived) << endl;
send(clientSocket, bufferSize, dataReceived + 1, 0); // сообщение для отправки клиенту, размер n+1 для нулевого символа
}
closesocket(clientSocket);
WSACleanup(); // закрываем нашу лавочку и очищаем winsock
// Закрываем сокет
// Выключаем сокет
}
Теперь включаем Putty
Часть 2 - Клиент
Снова проделываем те же махинации по созданию проекта, как и предыдущем случае.
Делаем main() func для клиента
C++:
#include <iostream>
#include <string>
#include <WS2tcpip.h>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
void main ()
{
setlocale(LC_ALL, "Russian");
string ipAddressServer = "127.0.0.1"; // IP адрес сервака по какому будем конектиться
int portServer = 40000; // Порт для прослушки на сервере
WSADATA WinSocksObjClient;
WORD version = MAKEWORD(2, 2);
int winSocksResultConnect = WSAStartup(version, &WinSocksObjClient); // переменная для запуска нашего winsocks
if (winSocksResultConnect != 0) // проверяем, смогли ли получить коннект
{
cerr << "Программа не может создать winsock! " << winSocksResultConnect << endl;
return;
}
SOCKET listenSockClient = socket(AF_INET, SOCK_STREAM, 0);
if (listenSockClient == INVALID_SOCKET)
{
cerr << "Программа не может создать сокет, " << WSAGetLastError() << endl; // вызываем метод выдачи ошибок
}
sockaddr_in addressIPForClientSocket; // проделываем те же махинации, как и со структурой сокета сервера
addressIPForClientSocket.sin_family = AF_INET;
addressIPForClientSocket.sin_port = htons(portServer); // передаем переме. номера порта
int sizeOfaddressIPForClientSocket = sizeof(addressIPForClientSocket); // перем. для хранения размера структуры
inet_pton(AF_INET, ipAddressServer.c_str(), &addressIPForClientSocket.sin_addr);
int connectResultToServer = connect(listenSockClient, (sockaddr*)&addressIPForClientSocket, sizeOfaddressIPForClientSocket); // реализуем коннект к серверу
if (connectResultToServer == SOCKET_ERROR) // проверяем подключение
{
cerr << "Программа не может подключиться к серверу" << WSAGetLastError() << endl;
closesocket(listenSockClient); // закрываем и очищаем
WSACleanup();
return;
}
char bufferSize[10000];
string dataFromClient;
do
{
cout << " ||>.. "; // приглашаешь к вводу данных
getline(cin, dataFromClient); // по средства ф-и getline получаем поток ввода и данные от клиента
if (dataFromClient.size() > 0) // чекнем, чтобы юзер что то отправил сперва
{
int sendResultFromClient = send(listenSockClient, dataFromClient.c_str(), dataFromClient.size() + 1, 0); // переменная для хранения результата отправки данных от клиента
if (sendResultFromClient != SOCKET_ERROR)
{
ZeroMemory(bufferSize, 10000);
int bytesReceivedFromCLient = recv(listenSockClient, bufferSize, 10000, 0); // фу-я для получение данных от клиента
if (bytesReceivedFromCLient > 0)
{
cout << "Сервер говорит: " << string(bufferSize, 0, bytesReceivedFromCLient) << endl; // выводим все в консоль
}
}
}
} while (dataFromClient.size() > 0);
closesocket(listenSockClient);
WSACleanup();
}
Теперь добавляем для удобства запуска серверную часть
Проверяем, правильно ли добавили все файлы. Теперь выбираем свойства нашего решения
И теперь запускаете F5 и ловите пруфы от своего чата.