Click to See Complete Forum and Search --> : problem with Socket


ccubed
June 25th, 2007, 04:32 AM
Okay, since i'm making a mud client, i expect some people to be asinine and try to connect to a server while they have no internet connection. Here's my problem. In my check to see if the socket returned INVALID_SOCKET, I always get an error from windows and my program exits. I was wondering if anyone could tell me why? (it's the bool open_socket function)


/*
Nova Client
Created: June 22, 2007
Last Updated: June 22, 2007
Description: This file contains all the socket functions this client uses
*/

#include <winsock.h>
#include <windows.h>
#include <iostream>
#include <string>

//a socket class
class novasock{

public:

void sock_info( int num, std::string hostn, novasock &NSock){

port = num;
address = hostn;

}

bool get_data( novasock &NSock ){

NSock.datasize = recv( Nova, NSock.buffer_in, 512, 0);

if ( NSock.datasize > 0 ){

std::cout << "#DEBUG: " << NSock.datasize << " Bytes received." << std::endl;

printf("%s\n",NSock.buffer_in);

return true;

}
else if( NSock.datasize == 0 ){

std::cout << "#debug: No data to receive" << std::endl;

return true;

}
else{

return false;

}

}

bool send_data( novasock &NSock ){

if ( sizeof( NSock.out ) >= 1 ){

NSock.datasize = send( Nova, NSock.out.c_str(), sizeof( NSock.out ), 0 );

std::cout << "#DEBUG: " << NSock.datasize << " Bytes sent." << std::endl << "#DEBUG:SENT: " << NSock.out << " . " << std::endl;

return true;

}
else{

return false;

}

}

bool open_socket( novasock &NSock ){

NSock.WVer = MAKEWORD(1,1);

NSock.error_ret = WSAStartup( NSock.WVer, &NSock.WsaData );

if ( NSock.error_ret != NO_ERROR ){

std::cout << "#Couldn't Initilize Socket. Error " << NSock.error_ret << " returned." << std::endl;

return false;
}

Nova = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

if ( Nova == INVALID_SOCKET ){

printf("socket failed with error: %ld\n", WSAGetLastError());

return false;

}

if ( Nova != INVALID_SOCKET){

NSock.LPHostEnt = gethostbyname(NSock.address.c_str());
NSock.sin.sin_family = AF_INET;
NSock.sin.sin_addr = *((LPIN_ADDR)*NSock.LPHostEnt->h_addr_list);
NSock.sin.sin_port=htons(NSock.port);

}

if ( NSock.LPHostEnt == NULL ){

std::cout << "Couldn't find " << NSock.address << "." << std::endl;

return false;

}
if ( connect( Nova, (SOCKADDR*) &NSock.sin, sizeof(NSock.sin) ) == SOCKET_ERROR ){

std::cout << "Error Connecting to " << NSock.address << "." << std::endl;

return false;

}

return true;
}

void close_Nsock(){

closesocket(Nova);

}

char buffer_in[512]; //these two are used too much to be private
std::string out;

private:

int port;
int datasize;
int error_ret;
LPHOSTENT LPHostEnt;
WSADATA WsaData;
WORD WVer;
std::string address;
SOCKET Nova;
struct sockaddr_in sin;
struct hostent host;

};

ahoodin
June 25th, 2007, 08:32 AM
Use WSAGetLastError() to check for WSAENOTSOCK and other such errors.

HTH,

MikeAThon
June 25th, 2007, 11:46 AM
What does the "error from windows" say before your program exits?

Try running your program in the debugger, and single-step throught the open_sock() function.

Incidentally, you should call WSAStartup() only once per program. It normally doesn't hurt to call it more often, so long as there is a matching call to WSACleanup(). Your program would apparently call it thousands of times, however, and I am not certain of the affect.

Mike

ahoodin
June 25th, 2007, 04:47 PM
Yep I agree here, he definitely shouldnt be requesting Winsock dll in the same function where he opens a socket. That could be a serious problem especially where you never call WSACleanup().

ccubed
June 25th, 2007, 05:48 PM
Okay, i'll try seperating the WSAStartup and socket calls. Also, i call WSACleanup in my main.cpp file when the program is exiting. But i've never really had a problem with the WSAStartup, it skips the check for invalid socket apparently and tries to go straight to gethostbyname, which of course, causes HUGE problems when there's no internet connection. However, i'll see if seperating the two will work.

ccubed
June 25th, 2007, 10:35 PM
Okay, this gonna be a long post but at least i now know what is wrong. It is definately the socket check and it definately fails inside the open_socket function. I've included updated code.


NOVA caused an invalid page fault in
module NOVA.EXE at 015f:00407fd1.
Registers:
EAX=00000000 CS=015f EIP=00407fd1 EFLGS=00010206
EBX=0076f9f0 SS=0167 ESP=0076f9a0 EBP=0076f9a8
ECX=00781dc8 DS=0167 ESI=8183677c FS=4057
EDX=0076fdac ES=0167 EDI=00000000 GS=40b6
Bytes at CS:EIP:
8b 40 0c 8b 00 8b 00 89 02 8b 5d 08 83 ec 0c 8b
Stack dump:
8183677c 00560000 0076fde8 00401658 0076f9f0 000007e4 0076f9d0 0040147e 00000000 01f0000f 00000000 575c3a43 00780084 535c5357 45545359 53575c4d

The above was the WIN 98 error report, below is vista's error report.

NOVA caused an invalid page fault in
module NOVA.EXE at 015f:00407fd1.
Registers:
EAX=00000000 CS=015f EIP=00407fd1 EFLGS=00010206
EBX=0076f9f0 SS=0167 ESP=0076f9a0 EBP=0076f9a8
ECX=00781dc8 DS=0167 ESI=8183677c FS=4057
EDX=0076fdac ES=0167 EDI=00000000 GS=40b6
Bytes at CS:EIP:
8b 40 0c 8b 00 8b 00 89 02 8b 5d 08 83 ec 0c 8b
Stack dump:
8183677c 00560000 0076fde8 00401658 0076f9f0 000007e4 0076f9d0 0040147e 00000000 01f0000f 00000000 575c3a43 00780084 535c5357 45545359 53575c4d


Below is the updated main.cpp


/*
Nova Client
Description: This is the main file. it handles the blunt of the program
*/
#include "socket_funcs.h"

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main(){

std::string addr;
int prt;
novasock NSock;
int cmd_ret;

cout << "#Nova Client Version 1.0" << endl;

begin:

cout << "#Nova Client is awaiting the User to enter the Host." << endl;

getline(cin, addr);

cout << "#Nova Client is awaiting the User to enter the port." << endl << endl;

cin >> prt;
cin.ignore(1000,'\n');
NSock.set_maxbuff(99999);

if ( NSock.init_wsa() == false ){

cout << "#Error initilizing WSADATA." << endl;

goto ask;

}

cout << "#Nova Client is connecting to " << addr << " on " << prt << "." << endl;

NSock.sock_info( prt, addr );

if ( NSock.open_socket() == true ){

cout << "#Nova Client has connected to " << addr << " on " << prt << "." << endl;

int i = 0; //get errors if it's declared in the while loop

while( i != 1 ){

if ( NSock.get_data() == false ){

i = 1;
cout << "#Error Receiving Data." << endl;

}

//make sure the above didn't fail
if ( i != 1 ){

getline(cin, NSock.out );

cmd_ret = NSock.cmd( NSock.out );

if ( cmd_ret == 1 ){

i = 1;
cout << "#Nova Client is exiting." << endl;

}
else if ( cmd_ret == 2 ){

NSock.out = NSock.last_cmd;

if ( NSock.send_data() == false ){

i = 1;
cout << "#Error Sending data." << endl;

}

}
else if ( cmd_ret == 0 ){

if ( NSock.send_data() == false ){

i = 1;

cout << "#Error sending data." << endl;

}

NSock.last_cmd = NSock.out;


}

}
}

}
else{

cout << "#Nova Client couldn't connect to " << addr << " on " << prt << "." << endl;

}

ask:

cout << "Exit Nova Client?(Y/N)" << endl;

std::string temp;

getline(cin,temp);

if ( temp == "Y" ){

cout << "#Nova Client is exiting." << endl;

NSock.close_Nsock();

WSACleanup();

system("pause");

}
else if ( temp == "N" ){

goto begin;

}
else{

cout << "#Invalid Option, please enter Y for yes and N for no." << endl;

goto ask;

}

}


Below is the updated socket_funcs.h


/*
Nova Client
Description: This file contains all the socket functions this client uses
*/
#include <winsock.h>
#include <windows.h>
#include <winsock2.h>//for select, implemented later.
#include <iostream>
#include <string>
#define DEBUG

//Socket Class for Nova Client
class novasock{

public:

int cmd( std::string command ){

if ( command == "\\exit" ){

return 1;

}
else if ( command == "\\!" ){

return 2;

}
else{

return 0;

}

}

void sock_info( int num, std::string hostn ){

port = num;
address = hostn;

}

void set_maxbuff( int num ){

MXBUFA = num;

}

bool get_data(){

datasize = recv( Nova, buffer_in, MXBUFA, 0);

if ( datasize > 0 || datasize <= 512 ){

#ifdef DEBUG
std::cout << "#DEBUG: " << datasize << " Bytes received." << std::endl;
#endif

printf("%s\n",buffer_in);

return true;

}
else if( datasize > 512 || datasize != 0 ){

int totrev;

int esize = datasize;//make sure we don't lose the original amount of data we SHOULD receive

printf("%s\n",buffer_in);

#ifdef DEBUG
int times;
#endif

while ( totrev != esize ){

datasize = recv( Nova, buffer_in, MXBUFA, 0 );
printf("%s\n",buffer_in);
totrev += datasize;
times += 1;

//if datasize is somehow greater then what we expected, treat it as new data. Should always be lower.
if ( datasize > esize ){

#ifdef DEBUG
std::cout << "Debug:MISC WARN:Datasize is bigger then what we SHOULD have received. DATASIZE " << datasize << ". ESIZE " << esize << ". treating it as new data we should get." << std::endl;
#endif

esize = datasize;

}

}

#ifdef DEBUG
std::cout << "#debug: Received a total of " << datasize << " bytes in " << times << " recv() processes." << std::endl;
#endif

return true;

}

else if( datasize == 0 ){

#ifdef DEBUG
std::cout << "#debug: No data to receive" << std::endl;
#endif

return true;

}
else{

return false;

}

}

bool send_data(){

if ( sizeof(out) >= 1 ){

datasize = send( Nova, out.c_str(), sizeof(out), 0 );

#ifdef DEBUG
std::cout << "#DEBUG: " << datasize << " Bytes sent." << std::endl << "#DEBUG:SENT: " << out << " . " << std::endl;
#endif

return true;

}
else{

return false;

}

}

bool init_wsa(){

WVer = MAKEWORD(1,1);

error_ret = WSAStartup( WVer, &WsaData );

if ( error_ret != NO_ERROR ){

std::cout << "#Couldn't Initilize WSAData. Error " << error_ret << " returned." << std::endl;

return false;

}

return true;

}

bool open_socket(){

Nova = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

if ( Nova == INVALID_SOCKET ){

printf("#socket failed with error: %ld\n", WSAGetLastError());

return false;

}

if ( Nova != INVALID_SOCKET){

LPHostEnt = gethostbyname(address.c_str());
sin.sin_family = AF_INET;
sin.sin_addr = *((LPIN_ADDR)*LPHostEnt->h_addr_list);
sin.sin_port=htons(port);

}

if ( LPHostEnt == NULL ){

std::cout << "Couldn't find " << address << "." << std::endl;

return false;

}
if ( connect( Nova, (SOCKADDR*) &sin, sizeof(sin) ) == SOCKET_ERROR ){

std::cout << "Error Connecting to " << address << " on port " << port << "." << std::endl;

return false;

}

return true;
}

void close_Nsock(){

closesocket(Nova);

}

//these two need to be able to be accessed by non members of novasock
char buffer_in[512];
std::string out;
std::string last_cmd;//last command sent to server

private:

int port;
int datasize;
int error_ret;
LPHOSTENT LPHostEnt;
WSADATA WsaData;
WORD WVer;
std::string address;
SOCKET Nova;
int MXBUFA;
struct sockaddr_in sin;

};


The part in bold is DEFINATELY where the problem is.

MikeAThon
June 26th, 2007, 07:22 AM
Run the program in the debugger, and single-step through the open_socket() function to find the line that is causing the page fault.

Mike

ccubed
June 26th, 2007, 10:31 PM
well, it's a mud client. I can't run the debugger though. for some reason, Dev c++ doesn't stop to get the input from me. Now my program returns an access violation from when i try to get an address from LPHostEnt that contains nothing. it also doesn't say LPHostEnt is invalid when i try to gethostbyname.

ahoodin
June 27th, 2007, 07:08 AM
Is the debugging information in the executible, or are you running a "release mode" executible?

In MSVC, there are different preprocessor defines that you set in the project. You will have to look up the method in dev.

With g++ there is -g to link in gnu debug info.

You *could* also pepper the program with couts to find out which was the last line to run.

MikeAThon
June 27th, 2007, 11:49 AM
See http://www.codeguru.com/forum/showthread.php?t=427239 for a related post

ahoodin
June 27th, 2007, 12:36 PM
The above URL appears to be a circular reference Mike.

Try some of these:
http://csjava.occ.cccd.edu/~gilberts/devcpp5/

http://codeguru.earthweb.com/forum/showthread.php?p=1201807#vcpp_debugging

http://www.codeguru.com/cpp/com-tech/atl/debug/article.php/c11975/

http://codeguru.earthweb.com/Csharp/Csharp/cs_misc/article.php/c4247/

Are also good related info.

HTH,

MikeAThon
June 27th, 2007, 01:41 PM
Thanks. I changed the link to http://www.codeguru.com/forum/showthread.php?t=427239 , to correct this.

Mike

grahamr (work)
June 28th, 2007, 05:54 AM
My 2p:

I have a little class that I wrote because I was sick of seeing (or not seeing) the references to DLL, libs and wotnot in the header.

The class is simple (copied from memory):
#pragma once

#pragma comment("ws2_32", lib)
#include "winsock2.h"

class UseWinsock {
public:
UseWinsock() {
if (!::WSAStartUp(0x0202, &wsadata))
throw "Error in Winsock";
}

~UseWinsock() {
::WSACleanUp();
}
private:
WSAData wsadata;
}

Now just derive this class as private class novosock : private UseWinsock { and it will automatically initialise and clean up as you go. Also you no longer have to worry about including the library as the #pragma comment does it all for you.

G.