Advertisement

WSASend & WSARecv :: Winsock

Started by May 15, 2002 09:00 PM
18 comments, last by kuphryn 22 years, 8 months ago
Hi. I am implementing a send and receive part of a simple message program. Everything seems to work okay, except the send and receive are not in sync. For send, I use WSASend. I first send a "header" buffer with the same of the real data buffer. Next, I send the real data buffer. For receive, I use WSARecv. I first receive the "header" buffer and determine its DWORD size. I then allocate a character array of that size. Next, I receive the real data buffer based on the size. The problem is the "header" buffer I receive does not hold the correct data size. For example, let say the data size is 10 byte (10 characters). Instead of 10, I will get something at least in 90+ when I cast the "header" to a DWORD. Here is the sample code for both send and receive. Please keep me updated if you find any known problem. ----- char *data = new char[4]; data = "test"; DWORD bufferSize = static_cast(strlen(data)), sentSize = 0; WSABUF bufferHDR, bufferDATA; char *header = new char[4]; CString sizeCnt; sizeCnt.Format("%d", bufferSize); strcpy(header, static_cast(sizeCnt)); bufferHDR.len = 4; bufferHDR.buf = header; // sending first data buffer contain the size if (WSASend(socket, &bufferHDR, 1, &sentSize, MSG_OOB, 0, 0) == 0) { bufferDATA.len = bufferSize; bufferDATA.buf = data; sentSize = 0; DWORD bufferProgress = 0; // make sure all data gets sent while (sentSize < bufferSize) { if (WSASend(m_ActiveSocket, &bufferDATA, 1, &bufferProgress, MSG_OOB, 0, 0) == 0) { sentSize += bufferProgress; bufferProgress = 0; } else { DetermineErrorWSASend(); sentSize = bufferSize; } } else DetermineErrorWSASend(); ----- ----- DWORD bufferSize = 0, receivedSize = 0, flags = 0; WSABUF bufferHDR, bufferDATA; char *header = new char[4], *data; bufferHDR.len = 4; bufferHDR.buf = header; // reading first buffer to determine real data size if (WSARecv(socket, &bufferHDR, 1, &receivedSize, &flags, 0, 0) == 0) { CString x; bufferSize = static_cast(*bufferHDR.buf); x.Format("%s%d", "bufferSize = ", bufferSize); AfxMessageBox(x); // allocating memory based on buffer size data = new char[bufferSize]; bufferDATA.len = bufferSize; bufferDATA.buf = data; receivedSize = 0; DWORD bufferProgress = 0; // make sure all buffer get read while (receivedSize < bufferSize) { if (WSARecv(socket, &bufferDATA, 1, &bufferProgress, &flags, 0, 0) == 0) { x.Format("%d", bufferProgress); AfxMessageBox(x); newData += *bufferDATA.buf; receivedSize += bufferProgress; } else { DetermineErrorWSARecv(); receivedSize = bufferSize; } } delete [] data; delete [] header; } ----- Thanks, Kuphryn
Here is your mistake:

CString sizeCnt;sizeCnt.Format("%d", bufferSize);strcpy(header, static_cast(sizeCnt));bufferHDR.len = 4;bufferHDR.buf = header; 


Why are you trying to send the data length as a string? Why not just send bufferSize directly?

memcpy(bufferHDR, bufferSize, sizeof(bufferSize)); 


Next mistake:
if (WSASend(socket, &bufferHDR, 1, &sentSize, MSG_OOB, 0, 0) == 0){ 


Why are you sending your data out-of-band (MSG_OOB). Set WSASend''s flag parameter to ''0''.

Another mistake:
if (WSARecv(socket, &bufferHDR, 1, &receivedSize, &flags, 0, 0) == 0){CString x; bufferSize = static_cast(*bufferHDR.buf); 


What you are doing here is setting bufferSize equal to the ASCII value of the first character in bufferHDR.buf (assuming your static_cast reads static_cast&ltDWORD>). This value is most likely 52, which corresponds to a character value of ''4''.

So from this point, your bufferSize is incorrect.

Lots of problems.






Dire Wolf
www.digitalfiends.com
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
Advertisement
Another mistake:

// make sure all data gets sentwhile (sentSize < bufferSize){ if (WSASend(m_ActiveSocket, &bufferDATA, 1, &bufferProgress, MSG_OOB, 0, 0) == 0){sentSize += bufferProgress;bufferProgress = 0;}   


You are just resending the same data over and over again.
You need to:

  // make sure all data gets sentWSABUF sendBuffer;    DWORD  sentSize = 0;        while (sentSize < bufferSize){     sendBuffer.buf = bufferDATA.buf + sentSize;    sendBuffer.len = bufferDATA.len - sentSize;            if (WSASend(m_ActiveSocket, &sendBuffer    , 1, &bufferProgress, MSG_OOB, 0, 0) == 0)    {        sentSize += bufferProgress;        bufferProgress = 0;    }    


You had the correct intention but the wrong implementation.

Dire Wolf
www.digitalfiends.com

EDIT: Oh the same situation applies to your WSARecv as well. You need to continually offset the buffer pointer or you'll just overwrite what you've already received.

[edited by - Dire.Wolf on May 16, 2002 10:30:32 AM]
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
Thanks.

You pointed out some keyed problem. Please give me some times to go over those possible problems.

I will try to post an update by tonight.

Thanks again,
Kuphryn
Okay. I will try passing 0.

Here is one questions. I am using the WSAAsyncSelect I/O Mode. For example, let say I send two packages. The first package holds the size of the data buffer. The second package is the actual data buffer. Okay. That sounds easy enough. In WSAAsyncSelect I/O Mode, do I need to set a flag to basically determine whether an incoming package is the *header* or the *data buffer*?

This is hard to explain and I hope maybe someone will understand what I am implying. Here is a different sample.

-----
-> send header
-> send data
-----

-----
WSAAsyncSelect-> incoming message
-> receive header
WSAAsyncSelect-> incoming message
-> receive data
-----

In other words as soon as WSAAsyncSelect send notice of an incoming message, should I implement *two* calls to WSARecv *consecutively* or do I need a bool switch (header/data) type of implementation if WSAAsyncSelect sends a message for *each* incoming message.

Thanks,
Kuphryn
Thanks.

Winsock the and WSAAsyncSelect I/O Mode is more difficult than I anticipated. I will need another week or so to debug the program. There are some major flaws in my program. Most of which has to do with *timing*, i.e. when to send/receive header and when to send/receive data buffer. Remember that I am using the WSAAsyncSelect I/O Mode. It affects much mmore than I thought before I read MSDN.

Kuphryn
Advertisement
Okay. I have determine one major problem, or should I say characteristic of WSASend and/or WSARecv.

For some reason, I cannot call WSASend one after another. For example, let say I call WSASend to send a 4 byte buffer to the client indicating the size of the incoming data. That works find and the client receives the first WSASend package without problems. However, for some reason I cannot call WSASend the second time to send the actual package. The error on the *sending* side is WSAEFAULT:

// WSAEFAULT: "Bad address"

The error on the *receiving* side is WSAEWOULDBLOCK:

// WSAEWOULDBLOCK: "Resource temporarily unavailable"

The bottomline is I cannot send two data buffer consecutively. I am using the WSAAsyncSelect I/O Mode. Is there a specific specification that I missed as far as sending and receiving?

Please message me if you have any ideas.

Thanks,
Kuphryn
You must have an error in your code. That is not normal behaviour for WSASend/Recv. BTW What OS are you coding and testing on?

Dire Wolf
www.digitalfiends.com
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com
Thanks.

I am developing the program under WindowsXP and Athlon 750mhz.

I am very, very close to getting everything working as planned. Here is where my progress. The program sends data perfectly (I believe). For example:

// Given WSABUF dataHDR, dataMessage


- send header buffer (dataHDR.Buf = data size)
- send data buffer (dataMessage.Buf = message)

// Given WSABUF inHDR, inMessage

- receive header buffer (inHDR = data size)
- use atol() to convert inHDR.Buf into an integer data size
- receive data buffer (inMessage.Buf = message)

Okay. I checked the actual receive size after calling WSARecv for the data buffer. The receive size is accurate, i.e. it received the exact number of byte that was sent. However, I cannot extract all characters from inMessage.Buf.

I use this code to extract the characters from inMessage.Buf

// CString message;
// message += *inMessage.Buf

The problem is the code above only extracts the *first* character in inMessage.Buf. I have considered that maybe only one character or byte is inside of inMessage.Buf; however, according to the check I did above on the actual received size, everything seems accurate.

Am I not converting inMessage.Buf correctly?

Kuphryn
You made an error:

// CString message;// message += *inMessage.Buf  


When you deference an array like that you are telling the compiler that you want what the pointer points to.

Since inMessage.Buf is a char*, when you deference a char* what do you get? You get a char. That is the reason you are only getting the first character; you only asked for the first character.

Try changing your code to:

CString message(inMessage.Buf);  


OR

CString message;message += inMessage.Buf;   


The first method will be more efficient.

Hope this helps.

Regards,




Dire Wolf
www.digitalfiends.com

[edited by - Dire.Wolf on May 18, 2002 3:10:39 AM]
[email=direwolf@digitalfiends.com]Dire Wolf[/email]
www.digitalfiends.com

This topic is closed to new replies.

Advertisement