Hi all,
I'm having a problem with NetLibSend and NetLibReceive. I am using
NetLibSend/Receive to talk to a web server. I have been able to use the HTTP
POST method to send small amounts of data (about 100 bytes), but whenever I try
to send lots of data (say 2k-4k), NetLibSend reports success, but NetLibReceive
times out.
According to my web server, I can see the access for the small POST, but the
large POST doesn't even appear.
When I run the program on the Palm simulator, all posts succeed! It is only on
the device that large posts just don't work.
Is there a problem with sending out k's of data using NetLibSend, that it would
report success but not really send the data? Is it a problem with just the Palm
Treo (device I'm using)?
Here is the code I'm using to do the HTTP protocol. There's a lot of stuff
here, but the main method is at the bottom, httpPost.
static string urlencode(char *s)
{
string ret = s;
char tmp[4];
char hex[5];
for (string::size_type i=0; i<ret.length(); i++)
{
if (TxtCharIsAlNum(ret[i]))
continue;
StrPrintF(hex, "%4x", ret[i]);
StrPrintF(tmp, "%%%s", hex+2); // skip over leading two zero's.
ret.replace(i, 1, tmp);
i++; // yeah, I know, we should skip over the hex digits, but
I'm lazy.
}
return ret;
}
/******************************************************
*
* Caches whether we have already opened the network
* library, and whether it was opened successfully or
* not.
*
* Returns false if the network couldn't be found or
* couldn't be opened.
*
* Returns true if the network was successfully opened.
*
* ****************************************************/
static Boolean isNetPresent()
{
Err error;
UInt16 ifErrs;
if (netFeature == NO)
return false;
else if (netFeature == YES)
return true;
if (SysLibFind("Net.lib", &netLibRefNum))
{
netFeature = NO;
return false;
}
error = NetLibOpen(netLibRefNum, &ifErrs);
if (ifErrs || (error && error != netErrAlreadyOpen))
{
netFeature = NO;
return false;
}
netFeature = YES;
return true;
}
static NetSocketRef connectTo(const char *host, UInt16 port, Err* error,
UInt16
statusFormID, UInt16 statusLabelID)
{
NetSocketRef socket;
NetSocketAddrINType destAddr;
NetHostInfoBufType hostbuf;
Int16 result;
if (!isNetPresent())
{
*error = netErrNotOpen;
return -1;
}
*error = 0;
connectionState = ConnectionState::SOCKET_OPEN;
socket = NetLibSocketOpen(netLibRefNum, // Network library
netSocketAddrINET, // Address domain
netSocketTypeStream, // Socket type
netSocketProtoIPTCP, // Protocol
SysTicksPerSecond()/2, // Timeout
error // Error result
);
if (*error)
return -1;
if (statusFormID != 0)
ShowResult(LOGIN_GETHOST, statusFormID, statusLabelID);
*error = 0;
connectionState = ConnectionState::FIND_HOST;
NetLibGetHostByName(netLibRefNum,
host,
&hostbuf,
SysTicksPerSecond()*3, //
Timeout
error);
if (*error)
return -1;
MemSet(&destAddr, sizeof(destAddr), 0);
destAddr.family = netSocketAddrINET; // This should match the second
argument to NetLibSocketOpen
destAddr.port = port;
destAddr.addr = hostbuf.address[0];
if (statusFormID != 0)
ShowResult(LOGIN_CONNECT, statusFormID, statusLabelID);
*error = 0;
connectionState = ConnectionState::SOCKET_CONNECT;
result = NetLibSocketConnect(netLibRefNum, // Network
library
socket, // Socket
reference
(NetSocketAddrType*)&destAddr, // Destination
address
sizeof(destAddr), // Length of
destAddr
SysTicksPerSecond()*CONNECT_TIMEOUT, //
Timeout
error // Error result
);
if (result)
{
return -1;
}
return socket;
}
static void closeSocket(NetSocketRef socket)
{
Err ignore;
NetLibSocketClose(netLibRefNum, socket, -1, &ignore);
}
static char readBuffer[256];
static UInt16 bufferSize;
static UInt16 readPtr;
static void initializeBufferedRead()
{
bufferSize = 0;
readPtr = 0;
}
// This is done just because when the PalmOS Simulator runs the code,
NetLibReceive
// does not time out if there is no data.
static Int16 _NetLibReceive(UInt16 libRefNum, NetSocketRef socket,
void *bufP, UInt16 bufLen, UInt16 flags, void *fromAddrP,
UInt16 *fromLenP, Int32 timeout, Err *errP)
{
NetFDSetType none;
NetFDSetType readfds;
Int16 numfds;
netFDZero(&none);
netFDZero(&readfds);
netFDSet(socket, &readfds);
numfds = NetLibSelect(libRefNum,
socket+1,
&readfds,
&none,
&none,
timeout,
errP);
if (numfds == 0)
{
*errP = netErrTimeout;
return -1;
}
if (numfds == -1)
return -1;
return NetLibReceive(libRefNum,
socket,
bufP,
bufLen,
flags, // flags
fromAddrP,
fromLenP,
0, // timeout
errP);
}
/*******************************************************
*
* Read one character from a socket.
*
* Returns the character read, or -1 if there was an error
* (and error contains the error) or if the socket closed
* (and error is 0 -- end of stream)
* ******************************************************/
static int bufferedRead(NetSocketRef socket, Err *error, Int16 timeout)
{
*error = 0;
if (readPtr == bufferSize)
{
// refill buffer
NetSocketAddrType senderAddr;
UInt16 senderAddrLen = sizeof(senderAddr);
Int16 result;
// Use _NetLibReceive for emulator
// Use NetLibReceive for release
connectionState = ConnectionState::READ_RESPONSE;
result = NetLibReceive(netLibRefNum,
socket,
readBuffer,
sizeof(readBuffer),
0, //
flags
&senderAddr,
&senderAddrLen,
timeout,
error);
if (result == -1 || result == 0)
return -1;
bufferSize = result;
readPtr = 0;
}
return readBuffer[readPtr++];
}
/*************************************
*
* Read a block of data.
*
* Returns number of characters actually read. If the number of characters
* actually read is less than the requested size, check error
* ************************************/
static int bufferedReadArray(NetSocketRef socket, char *buf, int size,
Err *error, Int16
timeout)
{
int i;
*error = 0;
for (i=0; i<size; i++)
{
int c;
c = bufferedRead(socket, error, timeout);
if (c == -1)
return i;
buf[i] = c;
}
return i;
}
Boolean httpPost(Err *error, char *host, UInt16 port, char *resource,
char *postData, UInt16 postSize,
MemHandle *response,
Int16 responseTimeout,
UInt16 statusFormID, UInt16
statusLabelID,
string *sresponse = NULL)
{
NetSocketRef socket;
Int16 result;
UInt16 sentBytes;
UInt16 bufLen;
char buf[512];
socket = connectTo(host, port, error, statusFormID, statusLabelID);
if (socket == -1)
return false;
if (statusFormID != 0)
ShowResult(LOGIN_SEND_REQUEST, statusFormID, statusLabelID);
StrPrintF(buf, "POST %s HTTP/1.0\r\n"
"User-Agent: Palm Hacked HTTP/0.0\r\n"
"Host: %s\r\n"
"Content-Length: %u\r\n\r\n",
resource, host, postSize);
connectionState = ConnectionState::WRITE_REQUEST;
bufLen = StrLen(buf);
sentBytes = 0;
while (sentBytes < bufLen) {
result = NetLibSend (netLibRefNum, // Network library
socket, // Socket reference
buf + sentBytes, // Buffer to send
bufLen - sentBytes, // Bytes to send from buffer
0, // Flags
NULL, // Destination address -- does
not apply to TCP sockets
0, // Length of destination address
SysTicksPerSecond()*3, // Timeout
error // Error result
);
if (result == -1)
{
closeSocket(socket);
return false;
}
sentBytes += result;
}
bufLen = postSize;
sentBytes = 0;
while (sentBytes < bufLen) {
result = NetLibSend (netLibRefNum, // Network library
socket, // Socket reference
postData + sentBytes, // Buffer to send
bufLen - sentBytes, // Bytes to send from buffer
0, // Flags
NULL, // Destination address -- does
not apply to TCP sockets
0, // Length of destination address
SysTicksPerSecond()*3, // Timeout
error // Error result
);
if (result == -1)
{
closeSocket(socket);
return false;
}
sentBytes += result;
if (statusFormID != 0)
{
char tmpbuf[32];
StrPrintF(tmpbuf, "Sent %d of %d bytes", sentBytes, postSize);
ShowCustomResult(statusFormID, statusLabelID, tmpbuf);
}
}
if (statusFormID != 0)
ShowResult(LOGIN_WAIT_RESPONSE, statusFormID, statusLabelID);
// Get status line, ignore reason phrase
initializeBufferedRead();
if (bufferedReadArray(socket, buf, 12, error, responseTimeout) != 12)
{
closeSocket(socket);
return false;
}
if (statusFormID != 0)
ShowCustomResult(statusFormID, statusLabelID, "Receiving
response...");
// make sure response is 200
buf[12] = 0;
if (StrCompare(buf+9, "200"))
{
closeSocket(socket);
return false;
}
// ignore headers, get to the data
while (true) {
int c;
c = bufferedRead(socket, error, responseTimeout);
if (c == -1)
{
closeSocket(socket);
return false;
}
if (c == '\r')
{
// see if next 3 characters are \n\r\n
if (bufferedReadArray(socket, buf, 3, error,
SysTicksPerSecond()) != 3)
{
closeSocket(socket);
return false;
}
buf[3] = 0;
if (!StrCompare("\n\r\n", buf))
break;
}
}
// read the data, concat into response string
if (sresponse == NULL)
*response = 0;
string str;
int total = 0;
while ( (bufLen = bufferedReadArray(socket, buf, sizeof(buf)-1,
error,
responseTimeout)) > 0)
{
buf[bufLen] = 0;
str += buf;
total += bufLen;
if (statusFormID != 0)
{
StrPrintF(buf, "Read %d bytes", total);
ShowCustomResult(statusFormID, statusLabelID, buf);
}
}
if (*error != 0)
{
closeSocket(socket);
return false;
}
if (sresponse == NULL)
{
*response = MemHandleNew(str.length()+1);
char *ptr = (char *)MemHandleLock(*response);
StrCopy(ptr, str.c_str());
MemHandleUnlock(*response);
}
else
*sresponse = str;
closeSocket(socket);
return true;
}
--
For information on using the Palm Developer Forums, or to unsubscribe, please
see http://www.palmos.com/dev/support/forums/