hi there,
like almost everybody else here i've got a problem.
I am working on a simplified IRC for unix, consisting of a server and a client. It's a task for university..
my problem is, my client receives strange signs from the server and when i want to parse them there are lots of more strange signs! I think the problem is that i dont convert strings and chars correctly or something in my assignments is wrong, but what?! Or maybe i got a logical problem with my STDIN_FILENO and select().

now here's the code for you gurus and experts out there, willing to help a poor student that has learned Java and is collecting his first experiences in C++.

here are is a part of the code, please dont be scared if it looks a bit long.
as a short description:

first i create my object client, then i create the sockets and after that i send/receive whatever select() is expecting. i got parts from the code from examples that i found in the www, trying to adapt them to my needs.
If you have any further questions, dont hesitate to ask me.

best regards
cylow22

#include "client.h"

using namespace std;
	    
		
client::client(string hostname, string port, string nickname)  {

	this-> hostname = hostname;
	this-> port = port;
	this->nickname= nickname;
	topic="";
	
	startClient(hostname, port);

}



int client::startClient(string hostname, string port)  {


const char *host; 
const char *service; 
service = port.c_str();
host = hostname.c_str(); 

int error; 
struct addrinfo hints; 
struct addrinfo *addr0; 

memset(&hints, 0, sizeof(hints)); 
//hints.ai_family = PF_UNSPEC;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM; 
hints.ai_protocol = IPPROTO_TCP; 
hints.ai_flags = 0; 


printf("Looking up host.."); 

	if ((error = getaddrinfo(host, service, &hints, &addr0)) < 0) { 
		perror(gai_strerror(error)); 
		return 1; 
	} 

printf("  OK \n");
int sockno; 
struct addrinfo *addr; 

for (addr = addr0; addr; addr = addr->ai_next) { 
	
	printf("Creating socket (ai_family=%d, ai_socktype=%d, ai_protocol=%d)...\n", 
	addr->ai_family, 
	addr->ai_socktype, 
	addr->ai_protocol
	); 
	
	if ((sockno = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0) { 
		perror("socket"); 
		continue; 
	}
	printf("  OK \n");
	printf("Connecting to host..."); 
	if ((connect(sockno, addr->ai_addr, addr->ai_addrlen)) < 0) { 
		perror("connect"); 
		close(sockno); 
		continue; 
	} 
	break; 
} 
if (!addr) { 
	printf("No connection found\n"); 
	return 1;
}
printf("  OK \n");
 
freeaddrinfo(addr0); 


bool first_login = true;
 std::string input;
 std::string cmd;
 
fd_set read_set0;

FD_ZERO(&read_set0);
FD_SET(STDIN_FILENO, &read_set0);
FD_SET(sockno, &read_set0);

	do { 
		char buffer[1024];
		int res;
		ssize_t length; 
		//int length;
		fd_set read_set = read_set0; 
		if ((res = select(sockno + 1, &read_set, NULL, NULL, NULL)) > 0) { 
							
			/*	
				//  auto-login isn't working yet
				if (first_login==true)  {
						string logincmd = "/LOGIN ";
						cmd = getCommand(logincmd).c_str();
						cout<<"getCommand liefert: "<<cmd<<endl;
						strcpy(buffer, cmd.c_str());
						first_login = false;
				}
				else { */
		
					if (FD_ISSET(STDIN_FILENO, &read_set)) { 
					printf("Read STDIN...\n");

						if ((length = read(STDIN_FILENO, buffer, sizeof(buffer))) < 0) {
							perror("read"); 
							continue;
						}					
					//input = buffer;
					cmd = getCommand(buffer).c_str();
					cout<<"cmd: "<<cmd<<endl;
					strcpy( buffer, cmd.c_str());
					cout<<"buffer: "<<buffer<<endl;
					buffer[length]='\0';
					//strcpy( buffer, input.c_str());
				}
	
				printf("Write socket...\n");
				buffer[length]='\0';
				if ((length = write(sockno, buffer, length)) < 0) { 
						perror("write");
						continue;
					}
				//} 

	
		if (FD_ISSET(sockno, &read_set)) { 
			printf("Read socket...\n"); 
			
			if ((length = read(sockno, buffer, sizeof(buffer))) < 0) { 
				perror("read"); 
				continue;
			cout<<"length: "<<length<<endl;
			} 
			else if (length == 0) { 
				printf("Server closed connection\n"); 
				break;
			} 
			printf("Write STDOUT...\n");
			if ((length = write(STDOUT_FILENO, buffer, length)) < 0) { 
				perror("write");
				continue;
			}
			
		buffer[length]='\0';
		std::string tmp = buffer;
		cout<<handleServerMessage(tmp)<<endl;	
		//cout<<"inc_buffer: "<<buffer<<endl;			
		}
		} 
	else if (res < 0 && errno == EINTR) { 
		continue;
	} 
	else { 
		perror("select"); 
		return 1;
	}
	}
		while (1); 
		close(sockno); 
	return 0;
}


std::string client::getCommand(string message)  {
	
	int msg_length = message.find(' ', 0);
	std::string command = message.substr(0, msg_length);
	int channel_msg_length = message.find('/', 0);
	std::string oldname;
	std::string oldtopic;
	std::string cmd_code;

   
	
	
	if ((command == "/JOIN" || command == "/join") && message.size() > 6) {
        channel = message.substr(msg_length+1, MAXCHANNELNAME);
           	cout<<"You are in channel "<<channel<<'.'<<endl<<flush;
			cmd_code ="10101";   
			cmd_code+=channel;
			return cmd_code;
	}
    

	else if (((command == "/LOGIN") || (command == "/login")) && message.size() > 6) {	
			cmd_code ="10001";
			cout<<nickname<<endl;
			cmd_code+=nickname;
			cout<<cmd_code<<endl;
			return cmd_code;
	}
			else {		
				cmd_code ="10011";
				cmd_code+=topic;
				cout<<cmd_code<<endl;
				return cmd_code;
			}
	}	
	else if((channel_msg_length==-1)) {
			cmd_code="10111";
			cmd_code+=message;
			return cmd_code;
	}



}



std::string client::handleServerMessage(string message)  {

std::string header=message.substr(0, 5);
int server_msg_length = message.length();
std::string modified_msg;
	
	if (header == "10111")  {
		modified_msg = message.substr(5, server_msg_length);
		return modified_msg;
	}
	else if (header == "10101") {
		std::string temp = ("You are in channel ");
		temp += modified_msg;
		temp += '.';
		return temp;
	}
}

Recommended Answers

All 5 Replies

Could you post the contents of client.h? It might be needed just to sanity-check things.

If you look at

if ((length = read(STDIN_FILENO, buffer, sizeof(buffer))) < 0) {
							perror("read"); 
							continue;
						}					

					cmd = getCommand(buffer).c_str();
					cout<<"cmd: "<<cmd<<endl;
					strcpy( buffer, cmd.c_str());
					cout<<"buffer: "<<buffer<<endl;
					buffer[length]='\0';

You're reading characters into a buffer and then converting that to a string, and then _later_ putting a null character to mark the end of the inputted characters. Think about what that means.

You might have similar problems in other locations in your code.

hey rashakil,
i am sorry because i dont know what that means. i am not sure if I have to add the '\0' to the char array buffer or to a string. how does the '\0' really work?
here is my client.h:

#ifndef CLIENT_H_
#define CLIENT_H_


#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>

#define BUF_SIZE 1024
#define SERVER "The_server_name_or_IP"
#define SERVERPORT 5001
#define MAXCHANNELNAME 12
#define MAXNICKSIZE 9
#define MAXTOPICSIZE 12
#define MAX_MSG_SIZE 140

using namespace std;

class client {


public:

client(string, string, string);
int startClient(string, string);
std::string getCommand(string);
std::string handleServerMessage(string);
string hostname;
string nickname;
string topic;
string port;



private:
int msg_length;
int channel_msg_length;
std::string oldname;
std::string oldtopic;
std::string cmd_code;
std::string channel;
std::string cmd;


};


#endif /*CLIENT_H_*/

A char* points to an array of characters (or maybe a single character, of course, but typically an array). You know how arrays work, I presume.

The function 'read' will fill the array with some number of characters (that have been read off the input stream).

Suppose your array contains the following before the read operation:

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
'#' '$' '%' 'T' '2' '5' '9' '&' 'H' 't' '_' '^'  0  'k' 'l' 'a'

These are just arbitrary characters, but in particular you'll see a nul character at position 12. It's not the character '0', which has value 48 -- it's the character with value 0. C-style strings are character arrays with a 0 at the end. For example, if you wanted to find the length of a C-style string, you might use a function written as follows:

int strlen(char* s) {
    int i = 0;
    while (s[i] != 0)
        i++;
    return i;
}

If you passed the address of the array I drew to this function, you would get 12.

Now let's look at how 'read' works. Suppose 'read' detects 7 characters, the word "Helllo!", at the input stream. So it returns 7 and writes those characters to the array whose address was passed as a parameter.

Our array (if we passed it to 'read') now looks like this:

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
'H' 'e' 'l' 'l' 'l' 'o' '!' '&' 'H' 't' '_' '^'  0  'k' 'l' 'a'

But as far as string functions are concerned, the 'string' contains 12 characters. For example, if you passed a char* to the std::string constructor, you would get a string with 12 characters. The std::string constructor reads characters until it comes across a character with the value 0.

If we look at your code again, we'll see that you have cmd = getCommand(buffer).c_str(); immediately after the read operation. You'll note that getCommand expects a std::string. C++ does an automatic conversion, then, from the C-style string to the std::string. So getCommand would receive a string with 12 characters, "Hello!&Ht_^".

If you wrote buffer[length] = '\0' immediately after the read operation, the string conversion would stop at the end of the characters you've input.

thanks for the reply rashakil, that helped me a bit. but my problem is, that I use read to get the userinput from keyboard and after that, check the input and want to send a header and behind this header, i want to add the message or something. for example if you want to send a simple "hello" message in a channel the getCommand converts the input to "10111hello". my problem is that the length of the char array is no constant at all. so the ssize_t length has to be 10 in this case to allow me to use buffer[10]='\0'. do you understand my problem?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.