Please help me with the chat server

I'm trying to develop a chat sever, not just an echo one, but with rooms and nicknames and another things. I probably made a mistake in some function written for working with rooms. I have been bursting my mind for several days trying to find it. The problem is that if you go to an existing room when you are using a program, one more room with the same name is created instead of rewriting the connection from one room to another.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
/*
 * 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* choose_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* _new_room = new room;
	room_name_glossary :: iterator i;
	for (i = rooms.begin(); i != rooms.end(); i++) {
		if (!(strcmp ((*i).first, name))) return (*i).second;
	}
	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) {

	con_info.current_room = choose_room (con_info.client);
	if (con_info.current_room == previous_room) return 1;

	(*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
	room_name_glossary :: iterator i;
	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"))) {
			if (!(change_room (con_info, previous_room))) cerr << "Can't change room!";
			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* choose_room (int& client) {

	char* chosen_room_name = new char [buf_length];
	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 (client, message1, strlen (message1), 0);
	else send (client, message2, strlen (message2), 0);

	for (i = rooms.begin(); i != rooms.end(); i++) {	//Shows the list of existing rooms
		send (client, (*i).first, strlen ((*i).first), 0);
	}

	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 = choose_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;

}
I've not run this, so I haven't reproduced your problem. But I do have some comments that may help.

1. If you used the standard string class where you need strings, it'll simplify your code to the point where it's understandable.

2. In function:
1
2
3
4
5
6
7
8
9
10
11
inline room* create_room (const char* name) {

	room* _new_room = new room;
	room_name_glossary :: iterator i;
	for (i = rooms.begin(); i != rooms.end(); i++) {
		if (!(strcmp ((*i).first, name))) return (*i).second;
	}
	rooms [&name [0]] = _new_room;
	return _new_room;

}
you create a room even if the client has selected and existing one.

3. In function:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 = choose_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;
}
you pass the address of a local record to a thread. The record then goes out of scope, leaving the thread working with memory that'll be overwritten.

4. In main(), you don't set the server listen address. It probably should be INADDR_ANY and you ought to set the protocol in socket().

5. You're passing handles by reference (like the socket handles), but they're just integral types so should be passed by value.
Last edited on
Thank you very much, I'll follow your recommendations and report then how it works.
I corrected a code and changed it a little, now it works much better, allowing to drift through the rooms. But there is still remaining one hidden mistake which induces some rubbish to get into messages exchange stream. Could you run this code please and have a look? I need only guesses what the reason may be.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#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);
int change_to_new_room (connection_info& con_info, room* previous_room);


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, enter a number of the room:\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 = 1; j < number; j++) i++;
	con_info.current_room = (*i).second;

	char* nickname = new char [nick_length];
	strcpy (nickname, (*previous_room) [con_info.client]);
	(*con_info.current_room)[con_info.client] = nickname;	//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;

}

int change_to_new_room (connection_info& con_info, room* 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"))) {
			if (!(change_room (con_info, previous_room))) cerr << "Can't change room!";
			continue;
		}

		if (!(strcmp (command, "#CHANGE_TO_NEW\r\n"))) {
			if (!(change_to_new_room (con_info, previous_room))) cerr << "Can't change room!";
			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;
	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 (client, message1, strlen (message1), 0);
	}
	else {
		send (client, message2, strlen (message2), 0);
		for (i = rooms.begin(); i != rooms.end(); i++) {	//Shows the list of existing rooms
				send (client, (*i).first, strlen ((*i).first), 0);
		}
	}

	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;

}

Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 = new connection_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 = INADDR_ANY;
	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;

}
Topic archived. No new replies allowed.