Basic chatroom logic error.

I apologies in advanced if I use incorrect terms.

I have just started teaching myself C++ and my mate offered to help by writing me a simple chat program made up of 1/3 code, 2/3 comments. He talked me through it yesterday, and I have gained a large understanding of how the compiler functions. At the end he told me he left a few things out, and asked me to try making it work properly. I attempted to do so, and the Client and Server seemed to work fine. But, I have noticed that when one client inputs a string, the other will only output the first word, and the second on a new line. For instance, if Client one sends,

"Hello how are you?"

The second would display,

"Hello"
"how"


Could this be because I am using cin and cout, or do I have to convert the char into a string before I send it? Or, perhaps, something completely different?
Unfortunately, we know nothing about how the text is sent, and how it's handled after you receive it, so it's really hard to know exactly what your problem is. Somehow I'm guessing that somewhere you were using cin >> a_string;, when getline(cin,a_string); is a better option, but that's just a shot in the dark.

EDIT: There was some work from the redundantly redundant redundancy department in the last sentence, but no more.

-Albatross
Last edited on
I could post the code of the client, with or without the comments. But, I don't know where I(or my teacher) made an error.
Both would be required actually. (Client/Server) comments would probably be helpful.
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
#include "ChatClient.h"
#include <iostream>
using namespace std;



/*
 * Something to note here is that we're some notations that are used within the Windows API (such as DWORD); these
 * are merely aliases for types that you are familiar with. For instance, the two are exactly the same:
 *
 * DWORD dwSomeVariable;
 * unsigned long dwSomeVariable;
 *
 * The reason being that a "DWORD" is merely an alias for "unsigned long"
 *
 */


/*
 * To enable us to use functions that we define later in this file we will "prototype" them now; all prototyping
 * does is introduce the name of a function (or variable) so that the compiler knows it will find it's actual
 * definition later.
 *
 */
DWORD WINAPI ProcessReceivedData(VOID*);
void Out(char* pstr);



/*
 * This is our socket; when a variable is declared outside any function it is referred to as a "global". Global
 * variables exist for the entire duration of the program. Although they are not declared within a function, they
 * are accessible by them. The reason we need to be global is because two functions (main and ProcessReceivedData)
 * both require access to it.
 *
 */
SOCKET hSocket;



void main()
{
	char strChat[512];


	/*
	 * A "thread" is a unit of execution; it enables you to physically execute two sources of code concurrently
	 * in respect to time.
	 *
	 * All programs have atleast one thread; the reason you don't see it's creation is because the operating
	 * system does it for you.
	 *
	 * A thread begins executing at the function you tell it to when you call CreateThread; for instance, the
	 * aforementioned thread the OS creates would set the starting function as "main".
	 *
	 * In this program we'll be using two; one which will be responsible for getting keyboard input from the user
	 * and the other will manage the processing of data we receive from the server (ProcessReceivedData)
	 *
	 */
	CreateThread(NULL, 4096, &ProcessReceivedData, NULL, NULL, NULL);

	/*
	 * By this point the code within the ProcessReceivedData() function is executing independently. We could even
	 * call Sleep(INFINITE) now and it would still be executing because Sleep() operates upon the current thread
	 * only.
	 *
	 * ---------------------
	 *
	 * Now we'll spend our time prompting the user to give us a chat message to send.
	 *
	 */
	for(;;)
	{
		/*
		 * Get keyboard input from the user; storing it in the strChat array.
		 *
		 */
		cin >> strChat;


		int x = strlen(strChat)+1;
		/*
		 * Send the chat message to the server.
		 *
		 */
		send(hSocket, strChat, strlen(strChat)+1, NULL);
	}
}




//-----------------------------------------------------------------------------
// ProcessReceivedData(VOID*)
//
//
// Description:
//   This function executes concurrently with main() and it's purpose is to
//   process data that is received from the server.
//
// Parameters:
//   It's irrelevant.
//
// Return:
//   Also, irrelevant.
//
//
DWORD WINAPI ProcessReceivedData(VOID*)
{
	SOCKADDR_IN Addr; // Will be later filled out with the network address of the chat server.
	WSADATA WI; // Nothing interesting.
	char strBuffer[512]; // Storage for received data from the server.


	/*
	 * For us to be capable of using Winsock functions we must first call WSAStartup; it does nothing of interest
	 * relative to us.
	 *
	 */
	WSAStartup(0x2020, &WI);


	/*
	 * socket() creates a system socket object; a socket is a logical construct that represents an endpoint
	 * of communication.
	 *
	 * AF_INET      indicates we want a socket of the IP family.
	 * SOCK_STREAM  specifies a stream-style socket (TCP) as opposed to a datagram-socket (such as UDP)
	 * IPPROTO_TCP  explicitly specifies we want a socket using the TCP protocol for good measure.
	 *
	 */
	hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


	/*
	 * Now that we have created the socket we need to define the address of the server we wish to connect
	 * to
	 *
	 */
	Addr.sin_family = AF_INET; // Indicates the type of address; this should always be equal to the first paramter of socket()
	Addr.sin_port = htons(6112); // Specifies the port number on the server; htons is required as the network uses a different numerical encoding scheme.
	Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Specifies the address of the server; again an internal format is used so we must use inet_addr() to convert our textual representation.

	/*
	 * Now that we've created our socket and described the address of the server we can actually connect to
	 * the server (prior to this, there has been no actual network communication.)
	 *
	 */
	connect(hSocket, (SOCKADDR*) &Addr, sizeof(Addr));

	/*
	 * Now we spend the rest of our time handling data that the server sends to our socket.
	 *
	 * "for(;;)" is simply a loop that iterate unto infinity.
	 *
	 */
	for(;;)
	{
		/*
		 * This function will receive data that has been sent to the socket we connected earlier; if no data has
		 * been received yet, it will wait for some to arrive.
		 *
		 * Once data has been received it will be copied into the array we declared earlier (strBuffer)
		 *
		 */
		recv(hSocket, strBuffer, 512, NULL);

		/*
		 * At this point it is assumed that data has been received and it is now time to process it.
		 *
		 * In this very simple program every message sent or received is a fully formatted string of characters
		 * that is ready to be displayed; so that's just what we're going to do.
		 *
		 */
		cout << strBuffer;
		cout << "\n";


		/*
		 * As this is a loop, after this point execution will resume at the recv() call above.
		 *
		 */
	}
}
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
#include <winsock2.h>
#include <iostream>
using namespace std;





/*
 * This is our "server" socket; the one which is used to enable clients to connect to us.
 *
 */
SOCKET hServerSocket;


/*
 * And this is an array of sockets which will be used to store each connected client's socket.
 *
 * Keep in mind that a "SOCKET" is just an integer; when you create a socket you get a valid
 * integer value. Given this fact, we can indicate an invalid client SOCKET by using a value
 * that will never be returned which is value 0 (aka: NULL)
 *
 */
SOCKET hClients[5];



void main()
{
	WSADATA WI;
	SOCKADDR_IN Addr;
	u_long opt;
	SOCKET hNewClientSocket;
	char strChat[512];
	int iResult;


	/*
	 * First step as usual is to initialize Winsock.
	 *
	 */
	WSAStartup(0x2020, &WI);


	/*
	 * Next, we must create the socket that we will receive client connections via.
	 *
	 */
	hServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


	/*
	 * Unlike the client, this time we're defining the address of the local network interface; the most significant
	 * aspect of this address is the port number.
	 *
	 */
	Addr.sin_family = AF_INET; // Again, IP address type.
	Addr.sin_port = htons(6112); // We'll be receiving connections via port 6112 (this behaviour is referred to as "listening")
	Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // And we'll just use loopback which is the easy way of saying "this computer"


	/*
	 * Now all we have to do is associate the server socket with the address we defined above; this is accomplished using
	 * the bind() function.
	 *
	 */
	bind(hServerSocket, (SOCKADDR*) &Addr, sizeof(Addr));


	/*
	 * Last of all we must set the server socket into "listening" mode which will enable it to receive connections
	 * from clients.
	 *
	 */
	listen(hServerSocket, SOMAXCONN);


	/*
	 * Any socket can operate in one or two modes; blocking (synchronous) or non-blocking (asynchronous), by default
	 * all sockets operate in synchronous mode.
	 *
	 * In this program though, this is not suitable because we need to be capable of servicing multiple sockets (the n
	 * number of clients)
	 *
	 * The following function sets the socket mode to asynchronous.
	 *
	 */
	ioctlsocket(hServerSocket, FIONBIO, &(opt=1));

	/*
     * Now we begin the primary job which is accepting new client connections and also processing data that has
	 * been received from preexisting client sockets.
	 *
	 */
	for(;;)
	{
		/*
		 * First we'll see if a new connection has arrived; when a client calls connect() as is done within
		 * the partner program (ChatClient) a corrosponding accept() is performed on the server. This function
		 * will return a socket object that holds the connection to the new client.
		 *
		 */
		hNewClientSocket = accept(hServerSocket, NULL, NULL);

		/*
		 * If a connection was available and it was successfully accepted then the return value (which was placed
		 * in the variable hSocket) will contain a value that is NOT equal to INVALID_SOCKET 
		 *
		 */
		if(hNewClientSocket != INVALID_SOCKET)
		{
			/*
			 * As mentioned above, all our sockets must operate in asynchronous mode so we'll set that mode now
			 * on the new client's socket.
			 *
			 */
			ioctlsocket(hNewClientSocket, FIONBIO, &(opt=1));

			/*
			 * Now that we have a new client socket we need to store it somewhere; this is where the global array
			 * hClientss comes in. So that we don't overwrite any other socket that we've already accepted, we must
			 * look for an element of the array with value 0 which indicates it is 'unused'
			 *
			 */
			for(int i = 0; i < 5; i++)
			{
				if(hClients[i] == 0)
				{
					/*
					 * Store the new client socket in the array element we found to be unused.
					 *
					 */
					hClients[i] = hNewClientSocket;


					/*
					 * This is just some debug information output.
					 *
					 */
					cout << "\nOpen connection; ID=" << i;


					/*
					 * The "break" keyword enables you to exit a loop prematurely. We're doing this because we
					 * have already stored the new client socket; if we kept looping then we'd end up utilizing
					 * the entire hClients array (because each one is by default, unused)
					 *
					 */
					break;
				}
			}

			/*
			 * Send the client some sort of startup message.
			 *
			 */
			send(hNewClientSocket, "<Server> I've got a lovely bunch of coconuts, fiddely-dee.", 61, NULL);
		}


		/*
		 * Other than accepting new clients we must process data that has been sent by any of the clients. As this
		 * is a chat program, the job of the server is to broadcast chat messages it receives to everyone else whom
		 * is chatting (ie., all other clients.
		 *
		 * We'll loop through our array of client sockets and deal with them one by one remembering that a value of
		 * 0 for a client socket means it is unused.
		 *
		 */
		for(int i = 0; i < 5; i++)
		{
			/*
			 * You can use the 'continue' keyword to prematurely restart the loop; we'll do this to skip unused
			 * client sockets we come across.
			 *
			 */
			if(hClients[i] == 0) continue;


			/*
			 * Now we'll receive any data that the client has sent us which will be copied into the strChat array.
			 * The return value of this function is meaningful, which will be sorted in a moment.
			 *
			 */
			iResult = recv(hClients[i], strChat, 512, NULL);


			/*
			 * If recv() returns a value of SOCKET_ERROR then this indicates that there was outstanding data
			 * sent from the client.
			 *
			 */
			if(iResult != SOCKET_ERROR) // "If data HAS been received from the client; do the following"
			{
				/*
				 * There is another case where recv() returns the value 0, this indicates that the client has
				 * closed the connection (disconnected) and therefor what we need to do is destroy the socket
				 * we've been using for them and also set the element of hClientss to unused so that it can
				 * be reused for another client at some other point in time.
				 *
				 */
				if(iResult == 0)
				{
					closesocket(hClients[i]);
					
					hClients[i] = 0;


					/*
					 * This is just some debug information output.
					 *
					 */
					cout << "\nClose connection; ID=" << i;
				}

				/*
				 * The alternate scenario is that we successfully received data, this being the most common
				 * case.
				 *
				 */
				else // "If the client did NOT close the connection AND we received data then do the following"
				{
					/*
					 * Now all we must do is send the chat message to every other client who is currently
					 * connected to the server.
					 *
					 * IMPORTANT: Obviously we do not want to send it to ourselves aswell as that would be
					 *            quite redundant.
					 *
					 */
					for(int x = 0; x < 5; x++)
					{
						if(x != i && hClients[x] != 0) // "If this element is not the same as the client who we just received data from AND it is not unused then do the following"
						{
							send(hClients[x], strChat, iResult, NULL);
						}
					}
				}
			}

		} // End of "for(int i = 5; i < 5; i++)"

	} // End of "for(;;)"

} // End of main()
							
Wow you've got yourself quite the mate there!

The problem looks like it is indeed your method of receiving commands through cin.

you may want to look at cin.get(); edit: or cin.getline() or pretty much anything

I happen to have a custom getAllCin function that you may find useful, if you need it just ask.

http://www.cplusplus.com/reference/iostream/istream/get/
Last edited on
Ahh, replacing the first;
 
cin >> strChat;
with
 
cin.getline (strChat, 512);

Worked perfectly. Thank you.


Topic archived. No new replies allowed.