OpenSourse проекты

Назад

Проект: онлайн чат на c++

Данная программа представляет собой простой текстовый чат между клиентом и сервером. Программа состоит из двух файлов: клиентской и серверной части.

Клиентская часть программы (первый файл) работает следующим образом. Сначала она подключается к указанному серверу по IP-адресу и порту, который вводит пользователь. Затем она запускает отдельный поток, который слушает входящие сообщения от сервера и выводит их в консоль. После этого она ждет ввода пользователем сообщения и отправляет его на сервер. Клиент может завершить свою работу, отправив сообщение "/exit".

Серверная часть программы (второй файл) также начинается с подключения к порту и ожидания входящих подключений от клиентов. При подключении нового клиента сервер добавляет его в список клиентов и отправляет им сообщение о подключении нового участника чата. Затем сервер также запускает отдельный поток для каждого клиента, который слушает входящие сообщения от клиента и отправляет их всем остальным клиентам в чате. Когда клиент отключается от сервера, сервер удаляет его из списка клиентов и отправляет всем участникам сообщение об отключении клиента.

Код пользовательского клиента:


#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <wininet.h>
#include <thread>
#include <string>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wininet.lib")

void receive_messages(SOCKET socket) {
    char buffer[1024];
    while (true) {
        memset(buffer, 0, 1024); // Очистка буфера
        int bytes_read = recv(socket, buffer, 1024, 0);
        if (bytes_read <= 0) { // Если сервер отключился
            std::cout << "Disconnected from server\n";
            closesocket(socket);
            exit(0);
        }
        std::cout << buffer << std::endl;
    }
}

int main() {
    WSADATA wsa_data;
    if (WSAStartup(MAKEWORD(2, 2), &wsa_data) != 0) {
        std::cerr << "Failed to initialize Winsock\n";
        return 1;
    }
    std::cout << "List of current secure servers:\n";
    HINTERNET hInternet = InternetOpenA("HTTP Example", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
    if (hInternet == NULL)
    {
        std::cerr << "Failed to open internet connection" << std::endl;
        return 1;
    }

    HINTERNET hUrl = InternetOpenUrlA(hInternet, "https://projectmp.ru/list.txt", NULL, 0, INTERNET_FLAG_RELOAD, 0);
    if (hUrl == NULL)
    {
        std::cerr << "Failed to open URL" << std::endl;
        InternetCloseHandle(hInternet);
        return 1;
    }

    char buffer[1024];
    DWORD bytesRead;
    while (InternetReadFile(hUrl, buffer, sizeof(buffer), &bytesRead) && bytesRead != 0)
    {
        std::cout.write(buffer, bytesRead);
    }

    InternetCloseHandle(hUrl);
    InternetCloseHandle(hInternet);

    SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (client_socket == -1) {
        std::cerr << "Failed to create socket\n";
        WSACleanup();
        return 1;
    }

    std::string ip_address_str;
    std::cout << "\n";
    std::cout << "Enter server IP address: ";
    std::getline(std::cin, ip_address_str);

    sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    const char* ip_address = ip_address_str.c_str();
    wchar_t wide_ip_address[256];
    MultiByteToWideChar(CP_ACP, 0, ip_address, -1, wide_ip_address, 256);
    InetPton(AF_INET, wide_ip_address, &server_address.sin_addr.s_addr); // Адрес сервера

    int port;
    std::cout << "Enter server port number: ";
    std::cin >> port;
    server_address.sin_port = htons(port); // Порт сервера

    if (connect(client_socket, (sockaddr*)&server_address, sizeof(server_address)) == SOCKET_ERROR) {
        std::cerr << "Failed to connect to server\n";
        closesocket(client_socket);
        WSACleanup();
        return 1;
    }

    std::cout << "Connected to server\n";

    std::thread receive_thread(receive_messages, client_socket);
    receive_thread.detach();

    std::string message;
    while (true) {
        std::getline(std::cin, message);
        if (message == "/exit") {
            break;
        }
        send(client_socket, message.c_str(), message.size(), 0);
    }

    closesocket(client_socket);
    WSACleanup();
    return 0;
}

Код серверной части проекта


#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <thread>
#include <vector>
#include <cstring>
#include <string>

#pragma comment(lib, "ws2_32.lib")

void handle_client(SOCKET client_socket, std::vector<SOCKET>& clients) {
	char buffer[1024];
	std::string connect_message = "User " + std::to_string(client_socket) + " has joined the chat\n";
	for (SOCKET client : clients) {
		send(client, connect_message.c_str(), connect_message.size(), 0);
	}
	while (true) {
		memset(buffer, 0, 1024); // Очистка буфера
		int bytes_read = recv(client_socket, buffer, 1024, 0);
		if (bytes_read <= 0) { // Если клиент отключился
			std::cout << "User " << client_socket << " has left the chat\n";
			auto it = std::find(clients.begin(), clients.end(), client_socket);
			if (it != clients.end()) {
				clients.erase(it);
			}
			std::string disconnect_message = "User " + std::to_string(client_socket) + " has left the chat\n";
			for (SOCKET client : clients) {
				send(client, disconnect_message.c_str(), disconnect_message.size(), 0);
			}
			closesocket(client_socket);
			break;
		}
		std::cout << "User " << client_socket << " sent a message: " << buffer << std::endl;
		std::string message = std::to_string(client_socket) + ": " + buffer; // Формирование сообщения с префиксом id
		for (SOCKET client : clients) { // Отправка сообщения всем клиентам
			if (client != client_socket) {
				send(client, message.c_str(), message.size(), 0);
			}
		}
	}
}

int main(int argc, char* argv[]) {
	int port;
	std::cerr << "Enter server port: " << std::endl;
	std::cin >> port;
	if (argc > 1) {
		port = std::stoi(argv[1]);
	}
	WSADATA wsa_data;
	int result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
	if (result != 0) {
		std::cerr << "WSAStartup failed with error: " << result << std::endl;
		return 1;
	}
	SOCKET server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server_socket == INVALID_SOCKET) {
		std::cerr << "Failed to create socket: " << WSAGetLastError() << std::endl;
		WSACleanup();
		return 1;
	}
	sockaddr_in server_address;
	server_address.sin_family = AF_INET;
	server_address.sin_addr.s_addr = INADDR_ANY;
	server_address.sin_port = htons(port);
	if (bind(server_socket, (sockaddr*)&server_address, sizeof(server_address)) == SOCKET_ERROR) {
		std::cerr << "Failed to bind socket to port: " << WSAGetLastError() << std::endl;
		closesocket(server_socket);
		WSACleanup();
		return 1;
	}
	// Добавление кода для вывода внешнего ip и порта

	char hostname[256];
	result = gethostname(hostname, sizeof(hostname));
	if (result != 0) {
		std::cerr << "Failed to get hostname: " << WSAGetLastError() << std::endl;
		WSACleanup();
		return 1;
	}

	addrinfo* info;
	result = getaddrinfo(hostname, NULL, NULL, &info);
	if (result != 0) {
		std::cerr << "Failed to get address info: " << result << std::endl;
		WSACleanup();
		return 1;
	}

	sockaddr_in* address = (sockaddr_in*)info->ai_addr;
	char ip[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &(address->sin_addr), ip, INET_ADDRSTRLEN);

	std::cout << "IP address: " << ip << " Port: " << port << std::endl;

	if (listen(server_socket, SOMAXCONN) == SOCKET_ERROR) {
		std::cerr << "Failed to start listening on socket: " << WSAGetLastError() << std::endl;
		closesocket(server_socket);
		WSACleanup();
		return 1;
	}

	std::vector<SOCKET> clients; // Вектор для хранения клиентских сокетов

	while (true) {
		sockaddr_in client_address;
		int client_address_size = sizeof(client_address);
		SOCKET client_socket = accept(server_socket, (sockaddr*)&client_address, &client_address_size);
		if (client_socket == INVALID_SOCKET) {
			std::cerr << "Failed to accept incoming connection: " << WSAGetLastError() << std::endl;
			continue;
		}
		std::cout << "User " << client_socket << " has joined the chat\n";
		clients.push_back(client_socket);
		std::thread client_thread(handle_client, client_socket, std::ref(clients));
		client_thread.detach();
	}

	closesocket(server_socket);
	WSACleanup();
	return 0;
}

Собранный билд клиентской части