Hi all, this will be my first post but hopefully if someone can help me out I'd more than happy to help someone else out. I'm currently working on a project by myself that involves making a Winsock based server in C++. I chose to use IOCP since I will have a high volume of users accessing the server but the documentation/tutorials I've found online are either extremely over-complicated or they just don't tell me what I want to know. Right now my code is full of memory leaks so lately I've taken a break from expanding the code and have just focused on fixing the leaks before I move on. The issue comes in at....
I have my worker thread code below:
Code:
void workerthread::run(){
vector<string>pbuffer;
while (WAIT_OBJECT_0 != WaitForSingleObject(s->g_hShutdownEvent, 0))
{
top:
BOOL bReturn = GetQueuedCompletionStatus(
s->g_hIOCompletionPort,
&dwBytesTransfered,
(LPDWORD)&lpContext,
&pOverlapped,
INFINITE);
if (NULL == lpContext)
{
//cleanup()
cout<<"NULL, Serious error has occurred";
break;
}
u=(user *)lpContext;
if ((FALSE == bReturn) || ((TRUE == bReturn) &&
(0 == dwBytesTransfered)))
{
cout<<"Socket Error, user("<<u->username<<") disconnected: 0 bytes transferred"<<endl;
u->logout();
cout<<"User: "<<u->username<<" deleted.\n";
//
//had "delete u" code here but caused a memory fault<<<<<<<<<<<<<
goto top;
}
//If type of IO is send
if(pOverlapped==u->s_pol)
{
//Will check to make sure all data was sent after memory leak fixing.
cout<<dwBytesTransfered<<" bytes sent to user-> "<<u->username<<endl;
goto top;
}
//Else type of IO is recv
else
{
cout<<dwBytesTransfered<<" bytes received from user-> "<<u->username<<endl;
//Output packet data in a form of <integers> if not visible using cout.
cout<<"Data : "<<getreadable(u->recvwbuf->buf)<<endl;
temp=u->recvwbuf->buf;
ZeroMemory(u->recvwbuf->buf, MAX_BUFFER);
int i=0;
pbuffer.clear();
while(i<temp.length())
{
//Found end of a packet, throw it in the vector.
if(temp[i]=='\3')
{
pbuffer.push_back(u->parsing_buff);
u->parsing_buff="";
}
else
//Not at the end of a packet, continue to concat.
{
u->parsing_buff+=temp[i];
}
++i;
}
//FOR each packet received, check it out.
for(int i=0;i<pbuffer.size() ;++i)
{
packet="";
tempstr="";
for(int j=0;j<pbuffer.at(i).length();++j)
{
//base64_decode each entry in the vector line by line.
if(pbuffer.at(i)[j]=='\n')
{
packet+=base64_decode(tempstr);
tempstr="";
}
else
{
tempstr+=pbuffer.at(i)[j];
}
}
//Get what's left over and throw it in packet.
if(!tempstr.empty())
packet+=base64_decode(tempstr);
//Initial handshake, perform if it hasn't been already.
if(u->hs==false)
{
if(base64_decode(pbuffer.at(i)).compare(u->keystr)==0)
{
cout<<"Handshake() accepted\n";
u->hs=true;
}
else
{
data.clear();
data.push_back("Error: Handshake() failed.");
try{
u->sendmsg(u->constructpacket(101,data));}
catch(Error e)
{
//cleanup
cout<<"User disconnected"<<endl;
/*delete u;*/closesocket(u->socket);
goto top;
}
/*delete u;*/closesocket(u->socket);
goto top;
}
}
else
{
try{u->parsedata(packet,data);}
catch(Error e)
{
data.clear();
data.push_back(e.msg);
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
//Depending on what type of packet, perform an action.
int type=data.at(0)[0];
switch(type)
{
case 0:
cout<<"Login packet received.\n";
if(u->loggedin==false && data.size()==4)
{
try
{
u->login(data.at(1),data.at(2),data.at(3));
continue;
}
catch(Error e)
{
data.clear();
data.push_back(e.msg);
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
}
else
{
data.clear();
data.push_back("Error, you are already logged in or malformed login packet received.");
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
break;
case 3:
if(u->loggedin==true && data.size()==2)
{
try
{
time_t curr=time(NULL);
if(curr-u->msgtime>u->mysite->time)
{
u->msgtime=time(NULL);
u->msgcount=0;
}else
{
u->msgcount+=1;
if(u->msgcount>u->mysite->msgs_per_time)
{
data.clear();
data.push_back("You have been kicked for flooding.");
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
}
u->roommsg(data.at(1));
}
catch(Error e)
{
data.clear();
data.push_back(e.msg);
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
}
else
{
data.clear();
data.push_back("Error, attempt to send message without being logged in, logged.");
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
break;
default:
data.clear();
data.push_back("Error, invalid packet type received.");
u->sendmsg(u->constructpacket(101,data));
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
}
}
nBytesRecv=WSARecv(u->socket,u->recvwbuf, 1,&u->dwBytes, &u->dwFlags, u->r_pol, NULL);
if ((SOCKET_ERROR == nBytesRecv) && (WSA_IO_PENDING != WSAGetLastError()))
{
//cleanup()
cout<<"Socket Error, user disconnected: Recv()"<<endl;
u->logout();
/*delete u;*/closesocket(u->socket);
goto top;
}
}
}
return;
}
The code may be kind of hard to figure out since there is a ton of more code to explain what's going on. The way I figured I'm probably doing this incorrectly to start with and there is probably a much easier way to go about this. But anyway, my issue occurs when I log in around 100 users and try to log them out simultaneously, I get a seg fault, which this issue only occurs if I attempt to delete the user, if I leave the deletion code out everything runs 100% okay but of course I have massive memory leaks from not deleting the users. Below is the code I'm currently using to delete the user:
NOTE: The exact moment the segmentation fault is occurring is when the console is at this point in code:
cout<<dwBytesTransfered<<" bytes received from user-> "<<!!!!!!!!!HERE>>!!!!!!!!!!u->username<<endl;
I know that most likely the reason its happening is because it's trying to print a string that doesn't exist anymore but I don't see how this is even occurring!!!?????
Code:
user::~user()
{
//while there are any receive or send operations left for this user...
while (!HasOverlappedIoCompleted(r_pol) || !HasOverlappedIoCompleted(s_pol))
{
cout<<"LOOPING"<<endl;
Sleep(100);
}
closesocket(socket);
cout<<"All IO complete, deleting..."<<endl;
delete r_pol;
delete s_pol;
delete recvwbuf;
delete sendwbuf;
delete [] recv_szBuffer;
delete [] sendbuf;
}
Which sometimes that loop in the destructor actually goes into an infinite loop and never releases(I understand a deadlock condition can occur because all of the thread pool could attempt to go into that loop at once, I fixed that by spawning a new thread everytime). Below is my code for when a user logs out which is what is called when a user logs out or performs an illegal action:
Code:
void user::logout()
{
vector<string>data;
if(loggedin)
{
//MUTEX object for user list
WaitForSingleObject(mysite->UserMutex,INFINITE);
//Tell everyone but myself that I am logging out!
for(int i=0;i<mysite->users.size();++i)
{
data.clear();
data.push_back(username);
if(lower(mysite->users.at(i)->username)!=lower(username))
{
try{
mysite->users.at(i)->sendmsg(constructpacket(2,data));
}
catch(Error e)
{
continue;
}
}
}
//Erase me from the user list
if(loggedin)
{
for(int i=0;i<mysite->users.size();++i)
{
if(lower(mysite->users.at(i)->username)==lower(username))
mysite->users.erase(mysite->users.begin()+i);
}
}
//Release the mutex to the user list.
ReleaseMutex(mysite->UserMutex);
}
}
Any help at all is extremely appreciated. Thanks!