Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

network socket problem

Status
Not open for further replies.

mikeeeblue

Programmer
May 26, 2005
7
GB
Hi all.

Im trying to code a client to communicate with a smtp server. Upon connecting to the smtp server the server sends their informaiton to the client, so i need to use the recv() method to get this information. I use a char buffer of about 300 to read this information into. I then wish to send the server my commands from the client im programming and after every command use the recv() method to read the servers response, so it goes roughly like this

recv() //receive the server information
for(how many commands i want to send, 8)
send command
receive response
end for loop

The problem is is when i call the initial recv method i have space left over in the buffer, because it is set to 300 and the information the server sends is less than 300. So when i send my first command i cannot read the response correctly because it seems to carry on filling up the previously unfilled buffer. However if i set the original buffer to 50 (which is less than the amount sent by the server) then every time i do a recv() method it passes me the next part of the server info and not the response to the command. Anyone know how to get round this? Btw im using winsock and the code is below:

Code:
//get server info
connect(Socket, (SOCKADDR *)(&SockAddr), sizeof(SockAddr));
int RetVal = SOCKET_ERROR;
char String[300];
while (RetVal == SOCKET_ERROR)
{
RetVal = recv(Socket, String, 301, 0);
if ((RetVal == 0)||(RetVal == WSAECONNRESET))
{
cout << ("Connection closed at other end.") << endl;
}
}

//send commands and get responses
for (int i = 0; i < 8; i++){
send(Socket, protocol[i], strlen(protocol[i]) + 1, 0);
cout << protocol[i] << endl;
int RetVal = SOCKET_ERROR;
while (RetVal == SOCKET_ERROR)
{
RetVal = recv(Socket, String, 301, 0);
if ((RetVal == 0)||(RetVal == WSAECONNRESET))
{
cout << ("Connection closed at other end.") << endl;
}
}
cout << String << endl;
}
 
hi.. i did exactly the same thing a couple of weeks ago.

first of all, it is EXTREMELY important to check each response from the server before sending a new command. you are right, you don't know how many bytes to expect from the server. so to deal with this, i used select() to check if there is any data ready to be received adn received one byte at a time. this may sound terribly inefficient, but i had to get it going quickly and didn't explore other options.

this is a method that checks if there is any data ready to be received on the socket:

Code:
//you will need these declarations somewhere
		SOCKET tcpSocket;
		SOCKADDR_IN serverInfo;
		struct hostent *h;
		FD_SET Writer;
		FD_SET Reader;
		timeval timeoutforselect;
//-------------------------------------------
bool myclass::rcvready(int sec, int usec){
	int selret; //an integer that stores the value returned by select.
	FD_ZERO(&Reader); //clear the FD_SET reader
	FD_SET(tcpSocket,&Reader); //put the socket in teh set.
	timeoutforselect.tv_sec=sec;
	timeoutforselect.tv_usec=usec;
	//the following select statement will return the number of sockets that 
	//can be read from and remove the sockets that cannot be read from
	//from the set.
	selret=select(0,&Reader,NULL,NULL,&timeoutforselect); 
	//cout<<"Value returned by select: "<<selret<<endl;
	
	//since only one socket was put in the set, select will return one
	//if the socket is ready to be read from and zero if not. 
	//therefore, the following, if-else statement suffices to check if its 
	//ready to read.
	//it will also return 1 if the socket has been closed by the other party. 
	//so, if zero bytes are received in a call to recv(...) after select returns 1,
	//it means that the connection has been terminated by the remote host. 
	//Also, if at any point more than one socket is added to the set, 
	//the FD_ISSET macro would have to be used to check if a particualr socket
	//is or is not ready to be read from. If FD_ISSET returns 0 on a particular
	//socket, it means that that socket is not in the set and is NOT ready to read
	//from. If FD_ISSET returns a non-zero value, it means that the socket is in 
	//the set, and is ready to be read from.
	

	if (selret==1 || selret>0){ //to allow for more than one socket in the set
								//if this becomes necessary later.
		return true;
	}
	else{
		return false;
	}
	

}

then i called this function to receive the message from the smtp server:

Code:
int myclass::receiveSMTP(char * temp){
	int i=1;
	int a;
	a=rcvTCPfromServer(temp, 1);
	if (a==-1) return -1;
	 while (rcvready()){
		a=rcvTCPfromServer(temp+i, 1);
		if (a==-1) return -1;
		i++;
	 }
	temp[i]=0;
	 cout<<temp<<endl;
	return 0;
}

BUT, please do make sure you check adn interpret each response from teh server before sending another command. if you don't, i guarantee you will be screwed! :p

hope this helps, let me know if you need anything else.

ps. my smtp server requires authentication. in fact, i had to use base 64 encryption.. i didn't have time to figure out what libraries and stuff to use.. so i just hard coded the encrypted text for my username/password. you can do this for testing purposes at: if you need to.
 
i forgot to metnion that in the second piece of code i pasted in my previous post, there is a call to rcvTCPfromServer. this is just a function i wrote to make sure that i receive all the bytes that i expect.. ie if you make a call to receive and tell it to receive 30 bytes, it is entirely possible that it will receive less than 30 bytes and return. then you're scrweed! so i receive in a while loop and make as many calls to recv as necessary to get all the bytes i expect. this may not be a big issue in the above code as we are receiving one byte at a time... but it doesn't hurt.
 
sorry.. forgot yet another thing that might be confusing.. in my prototype for rcvready, i have the arguments default to zero.. so in my call to the function, the arguments passed to rcvready are both zero!

possibly a better way to do it would be to call recv with some min number (3-9 or so should be safe) and then call rcvready.. so you loop less...its up to you
 
thanks a lot for your help mate. You have helped me solve the problem :)

thanks again
 
hey.. glad to help...

by the way... if you still have issues.. i really really recommend downloading a free piece of software called Ethereal. once you do this, you can use a regular email client (i used outlook express) and view all the data exchanged between teh smtp server and the client. it helped me a lot... because then you know exactly what to send and what to expect.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top