Browse Source

Basic implementations

Ivan Arkhipov 4 years ago
parent
commit
ebfbf1a64c
7 changed files with 383 additions and 3 deletions
  1. 64 0
      clientreader.cpp
  2. 45 0
      clientreader.h
  3. 69 0
      clientwriter.cpp
  4. 45 0
      clientwriter.h
  5. 2 3
      main.cpp
  6. 104 0
      server.cpp
  7. 54 0
      server.h

+ 64 - 0
clientreader.cpp

@@ -0,0 +1,64 @@
+#include "clientreader.h"
+
+ClientReader::ClientReader() {
+	buf = new char[65536];
+	host_ip = new char[48];
+	strcpy(host_ip, "127.0.0.1");
+}
+
+ClientReader::~ClientReader() {
+	delete[] buf;
+	delete[] host_ip;
+}
+
+int ClientReader::exec() {
+	if (!createSocket()) {
+		return -1;
+	}
+
+	sendMessage("reader");
+	std::cout << std::endl;
+	
+	std::string message;
+	while ((message = receiveMessage()) != "") {
+		std::cout << message << std::endl;
+	}
+	return 0;
+}
+
+bool ClientReader::createSocket() {
+	sockd = socket(AF_INET, SOCK_STREAM, 0);
+	if (sockd == -1) {
+		std::cout << "Client: Error: cannot create socket!" << std::endl;
+		return false;
+	}
+
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+
+    // Convert IPv4 and IPv6 addresses from text to binary form 
+    if(inet_pton(AF_INET, host_ip, &addr.sin_addr)<=0)  
+    { 
+        printf("\nInvalid address/Address not supported \n"); 
+        return -1; 
+    }
+
+	if (connect(sockd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
+    { 
+        printf("Client: Error: Connection Failed\n"); 
+        return false; 
+    } 
+	return true;
+}
+
+void ClientReader::sendMessage(std::string message) {
+	send(sockd, message.c_str(), message.length(), 0);
+}
+
+std::string ClientReader::receiveMessage() {
+	memset(buf, 0, 65536);
+	recv(sockd, buf, 65536, 0);
+	return std::string(buf);
+}

+ 45 - 0
clientreader.h

@@ -0,0 +1,45 @@
+#include <iostream>
+#include <vector>
+#include <string>
+#include <thread>
+#include <mutex>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+class ClientReader
+{
+public:
+	ClientReader();
+	
+	~ClientReader();
+
+	int exec();
+
+private:
+	bool createSocket(); // Функция инициализирует сокет клиента и переводит его в режим ожидания соединений.
+						 // Возвращает true, если инициализация удалась, иначе - false;
+
+	void sendMessage(std::string message);
+
+	std::string receiveMessage();
+
+private:
+	char* buf;
+	char* host_ip; 
+	uint32_t port = 12346; // Номер порта, по которому происходят соединения читателей
+	int sockd;
+	struct sockaddr_in addr;
+};

+ 69 - 0
clientwriter.cpp

@@ -0,0 +1,69 @@
+#include "clientwriter.h"
+
+ClientWriter::ClientWriter() {
+	buf = new char[65536];
+	host_ip = new char[48];
+	strcpy(host_ip, "127.0.0.1");
+}
+
+ClientWriter::~ClientWriter() {
+	delete[] buf;
+	delete[] host_ip;
+}
+
+int ClientWriter::exec() {
+	if (!createSocket()) {
+		return -1;
+	}
+
+	sendMessage("writer");
+
+	std::string message;
+	std::cout << "Please, enter message to send: ";
+
+	while (std::cin >> message) {
+		sendMessage(message);
+
+		std::cout << "Please, enter message to send: ";
+	}
+
+	std::cout << "Client: exiting;" << std::endl;
+	return 0;
+}
+
+bool ClientWriter::createSocket() {
+	sockd = socket(AF_INET, SOCK_STREAM, 0);
+	if (sockd == -1) {
+		std::cout << "Client: Error: cannot create socket!" << std::endl;
+		return false;
+	}
+
+    struct sockaddr_in addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+
+    // Convert IPv4 and IPv6 addresses from text to binary form 
+    if(inet_pton(AF_INET, host_ip, &addr.sin_addr) <= 0)  
+    { 
+        printf("\nInvalid address/Address not supported \n"); 
+        return false; 
+    } 
+
+	if (connect(sockd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 
+    { 
+        printf("Client: Error: Connection Failed\n"); 
+        return false; 
+    } 
+	return true;
+}
+
+void ClientWriter::sendMessage(std::string message) {
+	send(sockd, message.c_str(), message.length(), 0);
+}
+
+std::string ClientWriter::receiveMessage() {
+	memset(buf, 0, 65536);
+	recv(sockd, buf, 65536, 0);
+	return std::string(buf);
+}

+ 45 - 0
clientwriter.h

@@ -0,0 +1,45 @@
+#include <iostream>
+#include <vector>
+#include <string>
+#include <thread>
+#include <mutex>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+class ClientWriter
+{
+public:
+	ClientWriter();
+	
+	~ClientWriter();
+
+	int exec();
+
+private:
+	bool createSocket(); // Функция инициализирует сокет клиента и переводит его в режим ожидания соединений.
+						 // Возвращает true, если инициализация удалась, иначе - false;
+
+	void sendMessage(std::string message);
+
+	std::string receiveMessage();
+
+private:
+	char* buf;
+	char* host_ip; 
+	uint32_t port = 12346; // Номер порта, по которому происходят соединения читателей
+	int sockd;
+	struct sockaddr_in* addr;
+};

+ 2 - 3
main.cpp

@@ -1,7 +1,6 @@
 #include "clientreader.h"
 #include "clientwriter.h"
 #include "server.h"
-#include "helper.h"
 
 #include <iostream>
 #include <string>
@@ -23,9 +22,9 @@ int main(int argc, char** argv) {
     if (command == "client") {
     	// Если пользователь ввёл client - узнаём, читатель он или писатель
 
-    	std::cout << "Please, tell me, would you like to be a writer (type \"writer\") or a reader (type \"reader\")?";
+    	std::cout << "Please, tell me, would you like to be a writer (type \"writer\") or a reader (type \"reader\"): ";
     	std::string user_type;
-    	std::cin user_type;
+    	std::cin >> user_type;
 
     	if (user_type == "writer") {
     		// Если пользователь ввёл writer - создаём класс клиента-писателя и запускаем его функцию exec

+ 104 - 0
server.cpp

@@ -0,0 +1,104 @@
+#include "server.h"
+
+void *get_client_addr(struct sockaddr *sa) {
+    if (sa->sa_family == AF_INET) {
+        return &(((struct sockaddr_in *) sa)->sin_addr);
+    }
+
+    return &(((struct sockaddr_in6 *) sa)->sin6_addr);
+}
+
+int Server::exec() {
+	if (!createSocket()) {
+		return -1;
+	}
+
+    std::cout << "Server: server created and listening on port " << port << std::endl;
+
+    while (true) {
+    	std::cout << "Server: awaiting connection...\n";
+        ClientData *data = new ClientData(); // would be deleted in thread after its finishing
+        data->sockd = accept(sock, (struct sockaddr *) &data->client_sockaddr, &data->client_sockaddr_size);
+        std::cout << "Server: got new connection, creating worker thread." << std::endl;
+        clients_threads.emplace_back(std::thread(&Server::handleRequest, this, data));
+        std::cout << "Server: created worker thread " << clients_threads.back().get_id();
+    }
+}
+
+bool Server::createSocket() {
+	std::cout << "Server: creating socket..." << std::endl;
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+
+    int on = 1;
+    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &on, sizeof(on));
+
+    /* initialize the server's sockaddr */
+    struct sockaddr_in server_sockaddr;
+    memset(&server_sockaddr, 0, sizeof(server_sockaddr));
+    server_sockaddr.sin_family = AF_INET;
+    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    server_sockaddr.sin_port = htons(port);
+
+    printf("Server: Binding socket %d to sockaddr %p with size %d\n", sock, (struct sockaddr *) &server_sockaddr, sizeof(server_sockaddr));
+    int bind_result = bind(sock, (struct sockaddr *) &server_sockaddr, sizeof(server_sockaddr));
+    if (bind_result < 0) {
+        std::cout << "Server: Error: socket bind failed!" << std::endl;
+        return false;
+    }    
+
+    listen(sock, max_connections);
+
+    if (sock < 0) {
+    	std::cout << "Server: Error, cannot create socket" << std::endl;
+    	return false;
+    }
+    return true;
+}
+
+void Server::handleRequest(ClientData* client_data) {
+    char ip[17];
+    inet_ntop(AF_INET, get_client_addr((struct sockaddr *)&client_data->client_sockaddr), ip, sizeof(ip));
+
+    printf("Worker %u: Established connection with %s beginning work.\n", std::this_thread::get_id(), ip);
+
+    const int request_buffer_size = 65536;
+    char request[request_buffer_size];
+
+	// Receiving client type: reader or writer
+    int bytes_recvd = recv(client_data->sockd, request, request_buffer_size - 1, 0);
+
+    if (bytes_recvd < 0) {
+        fprintf(stderr, "Worker %u: error recv: %s\n", std::this_thread::get_id(), strerror(errno));
+        delete client_data;
+        return;
+    }
+    request[bytes_recvd] = '\0';
+
+    if (strcmp(request, "reader") == 0) {
+	    printf("Worker %u: Client %s is READER.\n", std::this_thread::get_id(), ip);
+	    client_readers_lock.lock();
+	    clients_readers.push_back(*client_data);
+	    client_readers_lock.unlock();
+    } else if (strcmp(request, "writer") == 0) {
+	    printf("Worker %u: Client %s is WRITER.\n", std::this_thread::get_id(), ip);
+	    while (true) {
+	    	    int bytes_recvd = recv(client_data->sockd, request, request_buffer_size - 1, 0);
+
+			    if (bytes_recvd <= 0) {
+			        fprintf(stderr, "Worker %u: error recv: %s\n", std::this_thread::get_id(), strerror(errno));
+			        delete client_data;
+			        return;
+			    }
+			    request[bytes_recvd] = '\0';
+
+			    printf("Worker %u: received message: %s\n", std::this_thread::get_id(), request);
+	    	    client_readers_lock.lock();
+	    		for (int i = 0; i < clients_readers.size(); ++i) {
+				    send(clients_readers[i].sockd, request, bytes_recvd, 0);
+	    		}
+	    		client_readers_lock.unlock();
+	    }	
+    } else {
+	    printf("Worker %u: Client %s is UNRECOGNIZED. Error!\n", std::this_thread::get_id(), ip);
+    }
+}

+ 54 - 0
server.h

@@ -0,0 +1,54 @@
+#include <iostream>
+#include <vector>
+#include <string>
+#include <thread>
+#include <mutex>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+struct ClientData {
+    int sockd; // Файловый дескриптор соединения. Используем его для обмена данными с клиентом
+    struct sockaddr_in client_sockaddr; // Сведения о клиенте 
+    socklen_t client_sockaddr_size;
+};
+
+class Server {
+public:
+	Server() = default;
+	
+	int exec(); // Основная функция сервера. Запускает сервер и осуществляет обмен сообщениями
+
+private:
+	bool createSocket(); // Функция инициализирует сокет сервера и переводит его в режим ожидания соединений.
+						 // Возвращает true, если инициализация удалась, иначе - false;
+
+	void handleRequest(ClientData* client_data); // Функция обработки соединения с клиентом. Осуществляет обмен данными с конкретным клиентом.
+
+
+private:
+	const uint32_t port = 12346; // Номер порта, по которому происходят соединения читателей
+	const uint32_t max_connections = 1000; // Максимальное количество одновременных соединений
+
+	int sock; // сокет сервера
+
+	std::vector<ClientData> clients_readers; // Храним массив с клиентами-читателями, т.к. любой писатель должен знать обо всех читателях
+
+	std::mutex client_readers_lock; // Используем мьютекс для массива читателей, чтобы исключить одновременные 
+									// чтение-запись для массива (например: появление новых читателей 
+									// и "публикации" писателей могут происходить одновременно)
+
+	std::vector<std::thread> clients_threads; // Каждое соединение с сервером обрабатывается в отдельном потоке
+};