|
Hi,
i am writing a Client/Server program where the server are on a LAN, and the client connects over the internet.
When both apps are on the same lan there is no problem, but when the client connects over the internet it cannot find the server. Can anybody tell me what goes wrong and how to fix this?
|
|
|
|
|
You should give to the user the IP of your Internet connection not your LAN IP.
|
|
|
|
|
Sébastien Lachance wrote:
You should give to the user the IP of your Internet connection not your LAN IP
Well we have tried both adresses with the same result.
|
|
|
|
|
Could you try my game that is based on the NDK: www.rocler.qc.ca/netblitz/index.htm.
|
|
|
|
|
There is the issue with NDK library because the way how it receives data from non-blocking sockets.
The application that uses CSocket/CAsyncSocker class should make only one Receive call per OnReceive function. Otherwise it clears FD_READ notification and disables possible following OnReceive call.
One cannot avoid multiple calling of CSocket::Receive (ie recv) with CArchive and CSocketFile because CArchive does it internally by calling CSocketFile::Read when uncomplete mesage is being read.
It probably never happens with chat application nonetheless for higher transmission rate it makes NDK library unusable.
So how could we change the library to allow more frequest exchange of data? Since CNDKMessage interface is nice and it is worth to be kept (along with NDK's server User management) I suggest to use CMemFile as an intermediate storage place.
I am picturing it on CNDKClientSocket extension (see below).
If this idea draws an interest I can polish it out and add it to NDK.
Also, see Knowledge articale Q185728 for more complete reading about common programmers' MFC socket mistakes...
Radim Krampol
void CNDKClientSocket::SendMessage(CNDKMessage &msg)
{
BYTE* p = (BYTE *)malloc(4096);
CMemFile g(p, 4096, 256);
CArchive a(&g, CArchive::store);
msg.Serialize(a);
a.Flush();
long lLength = (long)g.GetLength();
BYTE* pBuffer = g.Detach();
BYTE* packet = new BYTE[lLength+4];
*((long*)(packet)) = lLength;
memcpy(packet+4, pBuffer, lLength);
int nWritten = CSocket::Send(packet, lLength+4);
a.Abort();
delete packet;
delete pBuffer;
if (nWritten == SOCKET_ERROR) {
int nError = GetLastError();
AfxThrowFileException(CFileException::generic, nError);
}
}
void CNDKClientSocket::ReceiveToBuffer()
{
BYTE buff[4096];
int nRead = CSocket::Receive(buff, 4096);
if (nRead == SOCKET_ERROR) {
int nError = CSocket::GetLastError();
AfxThrowFileException(CFileException::generic, nError);
}
m_buffer.Add(buff, nRead);
}
bool CNDKClientSocket::ReadMessage(CNDKMessage &msg)
{
if (m_buffer.IsMessage()) {
BYTE* p = m_buffer.GetMessage();
long l = m_buffer.GetMessageLength()-sizeof(long);
CMemFile g(p, l, 0);
CArchive a(&g, CArchive::load);
msg.Serialize(a);
m_buffer.RemoveMessage();
return true;
}
return false;
}
// Return if the buffer is empty.
bool CNDKClientSocket::IsBufferEmpty()
{
return !m_buffer.IsMessage();
}
// Suggested NDKMessage buffer interface
class CNDKBuffer
{
public:
CNDKBuffer();
virtual ~CNDKBuffer();
void Add(BYTE* p, int n);
bool IsMessage();
BYTE* GetMessage();
long GetMessageLength();
void RemoveMessage();
};
|
|
|
|
|
I am very interested in this. I'm using NDK in a project and I'm seeing occasional lockups (server seems to stop being able to send or receive messages until clients disconnect and reconnect). After reviewing the MSDN info you mentioned, I think that may be what the problem is.
My one question is about size limitations -- occasionally I need to send data that's multiple megabytes in size, so it would need to be able to handle this.
Thanks!
|
|
|
|
|
Stan -- you bet this's a reason of the lost data.
I will look into coding the proposed changes during the weekend provided noone will come with a better idea how to fix the library.
Any interest in it, Sebastian?
As for multimegabyte messages, it's obviously possible. On the application level you can just split the file into chunks and send them as NDK messages.
Nonetheless, the NDK library is great for exchanging of small and variable messages. I don't believe that the used communication model is good for exchanging huge data (an overhead, it fills up the communication channel).
How about using a separate socket, maybe even in a different thread?
Radim
|
|
|
|
|
I agree your idea. I have think about creating another thread to transmite huge files, but don't know how to implement this.
And, I meet another problem. While using NDK 2.0 in WinCE & eVC, I can't stop the socket. When I close application, it will hang. I just stop the listening socket in CMainFrame::OnClose, like this: m_Server.Stop();,but the application hangs. I don't know why? Is there any hint from you? Thanks a lot!
|
|
|
|
|
Radim,
Just FYI I'm trying the "quick fix" changes to OnReceive shown in this forum for now, I don't think the reported performance hit will affect my application. But I would much prefer a "real" solution such as you propose.
As for large messages, I have two issues:
1. In some cases I have to send several thousand strings at a time, with an average length of about 100 characters each. Breaking this up would mean some major code changes for me, but NDK handles it nicely as-is.
2. I have to transfer complete files several MB in size. For this I extended NDK to read/write a file directly. For the life of me I can't remember if I coded this myself or if someone else here contributed it, but here are the functions added to the code:
void CNDKDataBuffer::ReadBufferFromFile (LPCTSTR lpszFilename)
{
if (m_pData != NULL)
{
free(m_pData);
m_pData = NULL;
}
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
hFind = FindFirstFile(lpszFilename, &FindFileData);
if (hFind != INVALID_HANDLE_VALUE)
{
m_unLength = FindFileData.nFileSizeLow;
FindClose(hFind);
OFSTRUCT ofstruct;
HFILE hFile = OpenFile (lpszFilename, &ofstruct, OF_READ);
if (hFile != HFILE_ERROR)
{
m_pData = malloc(m_unLength);
_lread (hFile, m_pData, m_unLength);
_lclose (hFile);
}
else
{
m_pData = NULL;
m_unLength = 0;
}
}
else
{
m_pData = NULL;
m_unLength = 0;
}
}
void CNDKMessage::ReadFromFile(LPCTSTR lpszFilename)
{
m_elements.Add(CNDKMessageData(lpszFilename));
}
CNDKMessageData::CNDKMessageData (LPCTSTR lpszFilename)
{
m_dataBuffer.ReadBufferFromFile (lpszFilename);
m_dataType = NDKDataBuffer;
}
BOOL CNDKMessageData::SaveAs (LPCTSTR lpszFilename) const
{
if (m_dataType == NDKDataBuffer)
{
OFSTRUCT ofstruct;
HFILE hFile = OpenFile (lpszFilename, &ofstruct, OF_CREATE | OF_WRITE);
if (hFile != HFILE_ERROR)
{
_lwrite (hFile, (char *) m_dataBuffer.GetBuffer(), m_dataBuffer.GetLength());
_lclose (hFile);
}
else
return FALSE;
}
return m_dataType == NDKDataBuffer;
}
Basically it uses the NDKDataBuffer type, but lets my code handle it like a file transfer:
case NMID_NetFile:
{
TCHAR *pszTempPath = new TCHAR[_MAX_PATH+2];
GetTempPath (_MAX_PATH, pszTempPath);
lstrcat (pszTempPath, "Temp.dat");
message.SaveAs (index++, pszTempPath);
}
break;
Sending file from server:
LRESULT CDlgNetServer::OnCmdSendFile (int nClient, LPCTSTR lpszFilename)
{
CNDKMessage messageOut (NMID_NetFile);
AddCommonHeader (messageOut);
messageOut.ReadFromFile (lpszFilename);
SendMessageToUser (nClient, messageOut);
return TRUE;
}
Hope that helps somebody.
Stan
|
|
|
|
|
It seems that you read a MB file into buffer and send at one time, Can large files be split into small parts and then be sent out? Thanks a lot!
|
|
|
|
|
Yes, this solution reads the file into a buffer and sends it all at once (though it is split into small packets at the socket/TCP level).
It would require coding *outside* of the NDK to handle splitting it up into multiple NDK messages, but I couldn't think of a reason to do this that was worth the effort of designing a file-packet-queing mechanism. It would take a lot of work to get something like that functioning smoothly, handling packets out of order, disconnects, missed messages, etc. etc. I figure the TCP designers already did that dirty work, why duplicate it?
Of course if the file is too large for available memory then it would make sense, or if you send lots of files at once.... but this wasn't an issue for me.
|
|
|
|
|
In fact, the socket does not hang but stop sending notification.
I resolved the problem with adding the following code in both socket files:
// Called when data is received.
void CNDKClientSocket::OnReceive(int nErrorCode)
{
CString str;
UINT ui;
//remove read notifications
VERIFY(AsyncSelect(/*FD_READ | */FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE));
…ProcessPendingRead()…
//reset notifications to default
if(0 != GetSockName(str, ui))
VERIFY(AsyncSelect());
}
|
|
|
|
|
Sebastian,
It's indeed very elegant solution and it will help in th emost cases.
I don't believe that it's complete under all circumstances. Have you taken into account the following lines from WSAAsyncSelect help page?
--
Although WSAAsyncSelect immediately disables event message posting for the socket in this instance, it is possible that messages could be waiting in the application's message queue.
--
Radim
|
|
|
|
|
3 weeks ago, I post a question to the MFC newsgroup at Microsoft. No one answer me
For now, it's the only way to solve my problem. This problem occurs also with the chatsvr and chatclient from Microsoft sample.
With VC6, there was no problem.
|
|
|
|
|
Sebastien,
I'm trying this fix now to see if it helps. There's something I'm confused about though.
From what I can tell, there are a couple different issues:
1. Reads occurring while in OnReceive (which your solution seems to address)
2. Data buffer length being an exact multiple of the buffer length, causing Read to hang (see Q192704)
Are these both solved by your patch, or is #2 still a potential problem?
In addition, I found this:
Q245434 -- Serialization with CSocketFile and CArchive Might Stop in a Multiprocessor Environment
Resolution -- either don't use serialization, or override data peeking (see their example of a fix)
Have you run into this, and is their solution the best way to do it or is there an alternative that can be done within NDK?
Thanks for a great tool!
Stan
|
|
|
|
|
The patch only fixes the first problem.
I did not have the problem with the multiprocessor environment.
If you want to remove the serialization, you need to modify some classes, I did not try that.
|
|
|
|
|
So, any suggestions with handling the problem of data being the exact buffer length? Under normal circumstances I would just check the length and add a byte if it's at the critical limit. But of course the length is difficult to determine when using serialization. And in my application, the data is of random lengths often exceeding 4K, so it's very likely to happen on any given day.
I couldn't quite figure out form the MS information whether it's a problem with CSocketFile or only with CArchive/Serialization. Could it be solved by using Radim's solution, or something similar but still using CSocketFile? (Is it even possible to use read/write on CSocketFile, without using CArchive & serialization???)
I thought of serializing to a memfile just to check the length -- but will that be accurate or is there additional overhead data? (And at that point why not just send the Memfile instead of re-serializing....)
Stan
|
|
|
|
|
Stan,
You have gig deeper and read the MFC source code and some reding of documentaion wouldn't hurt either.
Who told you that 4K is the maximum message length? It is not true it can be arbitrary length.
The thing I avoid with my suggested NDK enhancement is additional reading from the socket in CSocketFile (as per Q185728) while keeping the serialisation stuff intact.
Radim
|
|
|
|
|
I wasn't saying that 4K is a maximum length -- as per Q192704, there can be a problem if the message is an exact MULTIPLE of the buffer length (assumed to be 4K).
|
|
|
|
|
|
Radim, have you implemented your buffer idea?
Does it help?
I've been using NDK for years with no problems until I had to send messages over 500K.
I implemented the tip from Seb (AsyncSelect with no FD_READ) but big messages are still an issue
Strange fact:
The recipient enters an infinite loop in CSocket::PumpMessages only on Windows 98.
No problems with XP or W2k...
Laurent
Organize and communicate your ideas with NetNote
|
|
|
|
|
Laurent,
It does help! I hesitate to trust the Sebastian's elegant solution.
I haven't found any information in documentation that a notification does not get lots when it comes at the moment when it's momentarily disabled.
I don't use NDK in my projects but I have implemented my idea.
I am sending the files to you directly to your email box.
Let me know how it works for you.
Radim
|
|
|
|
|
1.open "NDKServerSOcket.h" file, change the construction of CNDKServerSocket to these:
CNDKServerSocket(PURPOSE_E iPurpose = FOR_DATA);
CNDKServerSocket(CNDKServer* pServer, PURPOSE_E iPurpose = FOR_DATA);
2.open "NDKServerSocket.cpp" file, change the implention of the construction of CNDKServerSOcket to these:
CNDKServerSocket::CNDKServerSocket(PURPOSE_E iPurpose) :
CCeSocket(iPurpose)
{
m_pServer = NULL;
m_pFile = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
}
CNDKServerSocket::CNDKServerSocket(CNDKServer* pServer, PURPOSE_E iPurpose) :
CCeSocket(iPurpose)
{
ASSERT(pServer != NULL);
m_pServer = pServer;
m_pFile = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
}
3. Add "m_bConnectCalled = TRUE;" to the begin of the CNDKServerSocket's Initialize function, that is:
BOOL CNDKServerSocket::Initialize()
{
m_bConnectCalled = TRUE; // Added
BOOL bResult = FALSE;
...
}
4. open "NDKServer.cpp" file, in the body of CNDKServer::StartListening function, change
"m_pListeningSocket = new CNDKServerSocket(this);"
to this:
"m_pListeningSocket = new CNDKServerSocket(this, CCeSocket::FOR_LISTENING);"
And now, you can work well with NDK 2.0 for WinCE & eVC, hope this work for you. Welcome your comments. Thank you!
|
|
|
|
|
Thank you for your hints.
Sébastien
|
|
|
|
|
Seb -- great application.
Was wondering if anyone has ever included both the client and server in the same application.
I think what I am looking to do is have the client connect to the server, get the addresses of users connect to server, return a selected users IP and then have a peep-to-peer conversation directly with the other user. This would entail a "server" piece to listen for direct p2p requests...
Thanks, Mike
|
|
|
|