Gistrec

Multi-thread chat [WIN API]

Nov 12th, 2018
356
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.42 KB | None | 0 0
  1. #pragma comment(lib, "Ws2_32.lib")
  2.  
  3. #include <concurrent_vector.h>
  4. #include <vector>
  5.  
  6. #include <string>
  7. #include <winsock2.h>
  8. #include <windows.h>
  9. #include <iostream>
  10.  
  11. using std::string;
  12. using std::cout;
  13. using std::endl;
  14. using std::cin;
  15.  
  16. DWORD WINAPI listenClient(LPVOID lpParam); // args: [0] SOCKET* server
  17. DWORD WINAPI listenServer(LPVOID lpParam); // args: [0] SOCKET* server
  18. void runServer();
  19. void runClient();
  20.  
  21. int main() {
  22.     #pragma region Регестрируем сокеты
  23.     WORD socketVer;
  24.     WSADATA wsaData;
  25.     socketVer = MAKEWORD(2, 2);
  26.     WSAStartup(socketVer, &wsaData);
  27.     #pragma endregion
  28.  
  29.     setlocale(LC_ALL, "Russian");
  30.  
  31.     string type; // Client/Server
  32.     int port;
  33.  
  34.     cout << "Что запускаем [client/server]" << endl;
  35.     cin >> type;
  36.  
  37.     if (type.compare("server") == 0) {
  38.         runServer();
  39.     }else if (type.compare("client") == 0) {
  40.         runClient();
  41.     }
  42.  
  43.     system("pause");
  44.     return 0;
  45. }
  46.  
  47. void runClient() {
  48.     // Создаем TCP сокет
  49.     SOCKET _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  50.     if (_socket == SOCKET_ERROR) cout << "Не удалось создать сокет" << endl;
  51.  
  52.     int port;
  53.     cout << "Введите порт клиента" << endl;
  54.     cin >> port;
  55.     #pragma region Создаем адрес клиента (addr) с портом port и любым адресом
  56.     SOCKADDR_IN addr;
  57.     addr.sin_family = AF_INET;
  58.     addr.sin_port = htons(port);
  59.     addr.sin_addr.s_addr = INADDR_ANY;
  60.     #pragma endregion
  61.  
  62.     // Назначаем сокету адрес
  63.     int result = bind(_socket, (LPSOCKADDR)&addr, sizeof(addr));
  64.     if (result == SOCKET_ERROR) cout << "Не удалось забиндить сокет" << endl;
  65.  
  66.     // Подключаемся к серверу
  67.     string server_ip;
  68.     cout << "Введите ip сервера" << endl;
  69.     cin >> server_ip;
  70.  
  71.     #pragma region Создаем адрес сервера (server_addr) с портом 2003 и server_ip
  72.     SOCKADDR_IN server_addr;
  73.     server_addr.sin_family = AF_INET;
  74.     server_addr.sin_addr.S_un.S_addr = inet_addr(server_ip.c_str());
  75.     server_addr.sin_port = htons(2003);
  76.     #pragma endregion
  77.  
  78.     result = connect(_socket, (sockaddr*)&server_addr, sizeof(server_addr));
  79.     if (result == SOCKET_ERROR) cout << "Не удалось подключиться" << endl;
  80.  
  81.     cout << "Подключение к серверу прошло успешно" << endl;
  82.  
  83.     // Создаем поток, который будет слушать сокет
  84.     DWORD threadID;
  85.     CreateThread(
  86.         NULL,       // Структура, которая определяет безопасность (NULL - по умолчанию)
  87.         0,          // Размер стека (0 - стандартное значение)
  88.         (LPTHREAD_START_ROUTINE)&listenServer, // Указатель на фунцию, которая выполнится в потоке
  89.         (LPVOID)_socket,       // Аргументы функции
  90.         0,          // Флаги, которые управляют созданием потока.
  91.         &threadID  // Указатель на переменную, в которую передастся номер потока
  92.     );
  93.  
  94.     // А здесь будем читать то, что ввел клиент
  95.     for (string line; std::getline(std::cin, line);) {
  96.         send(_socket, line.c_str(), line.size(), 0);
  97.     }
  98. }
  99.  
  100. DWORD WINAPI listenServer(LPVOID lpParam) {
  101.     SOCKET server = (SOCKET)lpParam;
  102.  
  103.     char buf[256];
  104.     int buf_length = 256;
  105.  
  106.     int connected = true;
  107.  
  108.     // Слушаем сервер
  109.     cout << "Слушаем сервер" << endl;
  110.     while (connected) {
  111.         buf_length = recv(server, buf, 256, 0);
  112.         if (buf_length == SOCKET_ERROR) {
  113.             cout << "Не удалось получить сообщение" << endl;
  114.             connected = false;
  115.             break;
  116.         }
  117.         buf[buf_length] = '\0';
  118.  
  119.         cout << buf << endl;
  120.     }
  121.     return 0;
  122. }
  123.  
  124. /**
  125.  * Массив с указателями на сокеты клиентов
  126.  * Нужен для отправки сообщения всем клиентам
  127.  * Потокобезопасен
  128.  */
  129. //concurrency::concurrent_vector<SOCKET*> clients;
  130. std::vector<SOCKET> clients;
  131.  
  132. void runServer() {
  133.     // Создаем TCP сокет
  134.     SOCKET _socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  135.     if (_socket == SOCKET_ERROR) cout << "Не удалось создать сокет" << endl;
  136.  
  137.     #pragma region Создаем адрес сервера (addr) с портом 2003 и любым адресом
  138.     SOCKADDR_IN addr;
  139.     addr.sin_family = AF_INET;
  140.     addr.sin_port = htons(2003);
  141.     addr.sin_addr.s_addr = INADDR_ANY;
  142.     #pragma endregion
  143.  
  144.     // Назначаем сокету адрес
  145.     int result = bind(_socket, (LPSOCKADDR) &addr, sizeof(addr));
  146.     if (result == SOCKET_ERROR) cout << "Не удалось забиндить сокет" << endl;
  147.  
  148.     // Слушаем подключения
  149.     while (true) {
  150.         cout << "Ожидаем подключение клиента" << endl;
  151.  
  152.         result = listen(_socket, 5);
  153.         if (result == SOCKET_ERROR) cout << "Не удалось слушать сокет" << endl;
  154.  
  155.         SOCKADDR_IN remote_address;
  156.         int address_length = sizeof(remote_address);
  157.  
  158.         // Подключаем нового клиента
  159.         SOCKET client = accept(_socket, (sockaddr*) &remote_address, &address_length);
  160.         if (client == INVALID_SOCKET) cout << "Не удалось принять подключение" << endl;
  161.  
  162.         // Добавляем сокет клиента в контейнер
  163.         clients.push_back(client);
  164.  
  165.         // Создаем новый поток, для клиента, где будем слушать его сокет
  166.         DWORD threadID;
  167.         CreateThread(
  168.             NULL,       // Структура, которая определяет безопасность (NULL - по умолчанию)
  169.             0,          // Размер стека (0 - стандартное значение)
  170.             (LPTHREAD_START_ROUTINE)&listenClient, // Указатель на фунцию, которая выполнится в потоке
  171.             (LPVOID) client,       // Аргументы функции
  172.             0,          // Флаги, которые управляют созданием потока.
  173.             &threadID  // Указатель на переменную, в которую передастся номер потока
  174.         );
  175.     }
  176. }
  177.  
  178. DWORD WINAPI listenClient(LPVOID lpParam) {
  179.     cout << "Создали поток" << endl;
  180.     SOCKET client = (SOCKET)lpParam;
  181.  
  182.     char buf[256];
  183.     int buf_length = 256;
  184.  
  185.     int connected = true;
  186.  
  187.     // Слушаем клиента
  188.     cout << "Слушаем клиента" << endl;
  189.     while (connected) {
  190.         buf_length = recv(client, buf, 256, 0);
  191.         if (buf_length == SOCKET_ERROR) {
  192.             cout << "Не удалось получить сообщение" << endl;
  193.             connected = false;
  194.             break;
  195.         }
  196.         buf[buf_length] = '\0';
  197.        
  198.         // Отправляем сообщение всем клиентам
  199.         // Кроме клиента, от которого пришло сообщение
  200.         for (auto other_client : clients) {
  201.             // Сравниваем указатели на сокеты клиентов
  202.             // if (client == other_client) continue;
  203.            
  204.             send(other_client, buf, buf_length, 0);
  205.         }
  206.         cout << "Пришло сообщение: " << buf << endl;
  207.     }
  208.     // Удаляем клиента из массива клиентов
  209.     cout << "Клиент отсоединился" << endl;
  210.     clients.erase(std::remove(clients.begin(), clients.end(), client), clients.end());
  211.     return 0;
  212. }
Advertisement
Comments
  • User was banned
Add Comment
Please, Sign In to add comment