Нужна помощь с сетевым чатом!

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Serkyron, 15 Dec 2013.

  1. Serkyron

    Serkyron New Member

    Joined:
    14 Dec 2013
    Messages:
    1
    Likes Received:
    0
    Reputations:
    0
    Добрый день всем!
    Я пытаюсь написать сетевой чат с комнатами, но уже которую неделю не могу понять что не так в моем коде, что некорректно работает переход между комнатами (каждая комната это словарь).

    Code:
    
    /*
     * server.cpp
     *
     *  Created on: 22 Nov 2013
     *      Author: Sergey
     */
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <iostream>
    #include <cstring>
    #include <sstream>
    #include <netinet/in.h>
    #include <map>
    #include <pthread.h>
    #include <unistd.h>
    #include <time.h>
    using namespace std;
    
    int buf_length = 256, nick_length = 16;
    typedef map <int, const char*, less <int> > room;
    typedef map <const char*, room*, less <const char*> > room_name_glossary;
    room_name_glossary rooms;
    struct connection_info{
    	int client;
    	room* current_room;
    	};
    
    room* create_room (const char* name);
    int is_used (char* nick, room& conn);
    const char* get_nickname (int* handle, room& current_room);
    int make_info (char* info, const char* nick);
    void send_to_all (room* current_room, connection_info& con_info, char* buffer, int& result);
    room* join_room (int& client);
    int add_connection (int& server);
    int change_room (connection_info& con_info, room* previous_room);
    void *new_thread (void *arg);
    
    
    inline room* create_room (const char* name) {
    
    	room_name_glossary :: iterator i;
    	for (i = rooms.begin(); i != rooms.end(); i++) {
    		if (!(strcmp ((*i).first, name))) return (*i).second;
    	}
    	room* _new_room = new room;
    	rooms [&name [0]] = _new_room;
    	return _new_room;
    
    }
    
    inline int is_used (char* nick, room& conn) {	//Checking if there is such a nickname in the map
    
    	room :: iterator i;
    	for (i = conn.begin(); i != conn.end(); i++) {
    
    		if (!(strcmp ((*i).second, nick))) return 1;
    
    	}
    
    	return 0;
    
    }
    
    const char* get_nickname (int* handle, room& current_room) {
    
    	const char* used_nick_message = "Such a nickname is already being used!\n";
    	const char* message = "Please, enter your nickname: ";
    	char* nick = new char [nick_length];
    
    	int send_result = send (*handle, message, strlen (message), 0);
    	if (send_result == -1) cerr << "Message sending error! (get_nickname)\n";
    	int recv_result = recv (*handle, &nick [0], nick_length, 0);
    	if (recv_result == -1) {
    		cerr << "Nickname receiving error! (get_nickname)\n";
    		return "GETTING NICKNAME FAILED";
    	}
    
    	while (is_used (nick, current_room)) {
    
    	 	send_result = send (*handle, used_nick_message, strlen (used_nick_message), 0);
    	 	if (send_result == -1) cerr << "Message sending error! (get_nickname)\n";
    		send_result = send (*handle, message, strlen (message), 0);
    		if (send_result == -1) cerr << "Message sending error! (get_nickname)\n";
    		memset (nick, 0, nick_length);
    		recv_result = recv (*handle, &nick [0], nick_length, 0);
    		if (recv_result == -1) {
    			cerr << "Nickname receiving error! (get_nickname)\n";
    			return "GETTING NICKNAME FAILED";
    		}
    
    	}
    
    	return nick;
    
    }
    
    int make_info (char* info, const char* nick) {	//Composes an information about a sender and sending time
    
    	time_t time_sec = time (0);
    	struct tm* time = localtime (&time_sec);
    
    	string buffer;
    	stringstream hour, min, sec;
    	hour << time->tm_hour;
    	min << time->tm_min;
    	sec << time->tm_sec;
    	buffer = '[' + hour.str() + ':' + min.str() + ':' + sec.str() + ']' + ' ';
    	buffer.insert(buffer.length(), nick, strlen(nick));
    	strcpy (info, buffer.c_str());
    
    	return 1;
    
    }
    
    void send_to_all (room* current_room, connection_info& con_info, char* buffer, int& result) {
    
    	room :: iterator i;
    	char* info_line = new char [buf_length];
    	for (i = (*current_room).begin(); i != (*current_room).end(); i++) {	//Sending messages to all the connections
    
    		if ((*i).first != con_info.client) {
    
    			if (!(make_info (info_line, (*current_room) [con_info.client]))) cerr << "Can't compose sender information!";
    			int send_result = send ((*i).first, info_line, buf_length, 0);
    			if (send_result == -1) cerr << "Nickname sending error! (thread)\n";
    			send_result = send ((*i).first, &buffer [0], result, 0);
    			if (send_result == -1) {
    				cerr << "Data sending error! (thread)\n";
    				break;
    			}
    
    		}
    
    	}
    	delete [] info_line;
    
    }
    
    int change_room (connection_info& con_info, room* previous_room) {
    
    	room_name_glossary :: iterator i;
    	const char* message1 = "None room has been found.\nPlease, enter a name of the new room: ";
    	const char* message2 = "Please, choose one of the existing rooms or create a new one:\n";
    
    	if (rooms.empty()) send (con_info.client, message1, strlen (message1), 0);
    	else send (con_info.client, message2, strlen (message2), 0);
    
    	for (i = rooms.begin(); i != rooms.end(); i++) {	//Shows the list of existing rooms
    		send (con_info.client, (*i).first, strlen ((*i).first), 0);
    	}
    
    	char* received_index = new char [buf_length];
    	recv (con_info.client, received_index, buf_length, 0);
    	stringstream stream;
    	stream << received_index;
    	int number;
    	stream >> number;
    	i = rooms.begin();
    	for (int j = 0; j <= number; j++) i++;
    	con_info.current_room = (*i).second;
    
    	(*con_info.current_room)[con_info.client] = (*previous_room) [con_info.client];	//Rewriting a connection to another room
    	(*previous_room).erase(con_info.client);	//Removing a connection from the previous room
    	if (previous_room->empty()) {
    
    		for (i = rooms.begin(); i != rooms.end(); i++) {
    			if ((*i).second == previous_room) rooms.erase ((*i).first);	//Removing a room from the rooms_map
    		}
    		delete previous_room;
    
    	}
    	return 1;
    
    }
    
    void *new_thread (void *arg) {
    
    	connection_info con_info = *(connection_info*)arg;
    	char* buffer = new char [buf_length];
    	int result;
    	room :: iterator i;
    
    	do {
    
    		result = recv (con_info.client, buffer, buf_length, 0);
    		if (result == -1) {
    			cerr << "Data receiving error! (thread)";
    			break;
    		}
    
    		room* previous_room = con_info.current_room;	//In case a client wants to change a room
    		char* command = buffer;
    		if (!(strcmp (command, "#CHANGE_ROOM\r\n"))) {
    			con_info.current_room = join_room (con_info.client);
    			if (con_info.current_room == previous_room) continue;
    			else {
    				(*con_info.current_room) [con_info.client] = (*previous_room) [con_info.client];
    				(*previous_room).erase(con_info.client);
    				continue;
    			}
    		}
    
    		send_to_all (con_info.current_room, con_info, &buffer [0], result);
    
    	} while (result > 0);
    
    	delete [] buffer;
    	delete [] (*con_info.current_room) [con_info.client];
    	if (!((*con_info.current_room).erase(con_info.client))) cerr << "Map element erasing error! (thread)\n";
    	close (con_info.client);
    
    	return 0;
    
    }
    
    room* join_room (int& client) {
    
    	char* chosen_room_name = new char [buf_length];
    	room_name_glossary :: iterator i;
    	int result = recv (client, &chosen_room_name [0], buf_length, 0);
    	if (result == -1) {
    		cerr << "Can't receive a room_name!";
    		return 0;
    	}
    	room* chosen_room = create_room (chosen_room_name);
    	return chosen_room;
    
    }
    
    int add_connection (int& server) {
    
    	sockaddr_in client_addr;
    	unsigned int client_addr_len = sizeof (client_addr);
    
    	while (true) {									//Waiting for connections
    
    		connection_info con_info;
    		con_info.client = accept (server, (sockaddr*) &client_addr, &client_addr_len);
    		if (con_info.client == -1) {
    			cerr << "Accepting error!\n";
    			continue;
    		}
    		con_info.current_room = join_room (con_info.client);
    		const char* nickname = get_nickname (&con_info.client, *con_info.current_room);
    		if (!(strcmp (nickname, "GETTING NICKNAME FAILED"))) {
    			cerr << "Can't get a nickname, try to connect again!\n";
    			continue;
    		}
    		(*con_info.current_room) [con_info.client] = nickname;	//Adding a new connection
    		pthread_t thread;
    		pthread_create (&thread, 0, &new_thread, (void*) &con_info);	//Creating a new thread
    
    	}
    	close (server);
    
    	return 0;
    
    }
    
    int main () {
    
    	sockaddr_in server_addr;
    	unsigned int server_addr_len = sizeof (server_addr);
    	int server = socket (AF_INET, SOCK_STREAM, 0);
    
    	server_addr.sin_addr.s_addr = 0;
    	server_addr.sin_family		= AF_INET;
    	server_addr.sin_port		= htons (1234);
    
    	int result = bind (server, (sockaddr*) &server_addr, server_addr_len);
    	if (result == -1) {
    		cerr << "Socket binding error!\n";
    		return 0;
    	}
    
    	listen (server, SOMAXCONN);
    
    	add_connection (server);
    
    	return 0;
    
    }