gssize udp_receiveUserData(UDP* udp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(udp); Packet* packet = socket_removeFromInputBuffer((Socket*)udp); if(!packet) { return -1; } /* copy lesser of requested and available amount to application buffer */ guint packetLength = packet_getPayloadLength(packet); gsize copyLength = MIN(nBytes, packetLength); guint bytesCopied = packet_copyPayload(packet, 0, buffer, copyLength); utility_assert(bytesCopied == copyLength); packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_DELIVERED); /* fill in address info */ if(ip) { *ip = packet_getSourceIP(packet); } if(port) { *port = packet_getSourcePort(packet); } /* destroy packet, throwing away any bytes not claimed by the app */ packet_unref(packet); /* update the tracker output buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("user read %u inbound UDP bytes", bytesCopied); return (gssize)bytesCopied; }
gssize tcp_receiveUserData(TCP* tcp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(tcp); /* * TODO * We call descriptor_adjustStatus too many times here, to handle the readable * state of the socket at times when we have a partially read packet. Consider * adding a required hook for socket subclasses so the socket layer can * query TCP for readability status. */ /* make sure we pull in all readable user data */ _tcp_flush(tcp); gsize remaining = nBytes; gsize bytesCopied = 0; gsize totalCopied = 0; gsize offset = 0; gsize copyLength = 0; /* check if we have a partial packet waiting to get finished */ if(remaining > 0 && tcp->partialUserDataPacket) { guint partialLength = packet_getPayloadLength(tcp->partialUserDataPacket); guint partialBytes = partialLength - tcp->partialOffset; g_assert(partialBytes > 0); copyLength = MIN(partialBytes, remaining); bytesCopied = packet_copyPayload(tcp->partialUserDataPacket, tcp->partialOffset, buffer, copyLength); totalCopied += bytesCopied; remaining -= bytesCopied; offset += bytesCopied; if(bytesCopied >= partialBytes) { /* we finished off the partial packet */ packet_unref(tcp->partialUserDataPacket); tcp->partialUserDataPacket = NULL; tcp->partialOffset = 0; } else { /* still more partial bytes left */ tcp->partialOffset += bytesCopied; g_assert(remaining == 0); } } while(remaining > 0) { /* if we get here, we should have read the partial packet above, or * broken out below */ g_assert(tcp->partialUserDataPacket == NULL); g_assert(tcp->partialOffset == 0); /* get the next buffered packet - we'll always need it. * this could mark the socket as unreadable if this is its last packet.*/ Packet* packet = socket_removeFromInputBuffer((Socket*)tcp); if(!packet) { /* no more packets or partial packets */ break; } guint packetLength = packet_getPayloadLength(packet); copyLength = MIN(packetLength, remaining); bytesCopied = packet_copyPayload(packet, 0, buffer + offset, copyLength); totalCopied += bytesCopied; remaining -= bytesCopied; offset += bytesCopied; if(bytesCopied < packetLength) { /* we were only able to read part of this packet */ tcp->partialUserDataPacket = packet; tcp->partialOffset = bytesCopied; break; } /* we read the entire packet, and are now finished with it */ packet_unref(packet); } /* now we update readability of the socket */ if((tcp->super.inputBufferLength > 0) || (tcp->partialUserDataPacket != NULL)) { /* we still have readable data */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE); } else { /* all of our ordered user data has been read */ if((tcp->unorderedInputLength == 0) && (tcp->error & TCPE_RECEIVE_EOF)) { /* there is no more unordered data either, and we need to signal EOF */ if(totalCopied > 0) { /* we just received bytes, so we can't EOF until the next call. * make sure we stay readable so we DO actually EOF the socket */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE); } else { /* OK, no more data and nothing just received. */ if(tcp->flags & TCPF_EOF_SIGNALED) { /* we already signaled close, now its an error */ return -2; } else { /* we have not signaled close, do that now and close out the socket */ _tcp_endOfFileSignalled(tcp); return 0; } } } else { /* our socket still has unordered data or is still open, but empty for now */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, FALSE); } } debug("%s <-> %s: receiving %lu user bytes", tcp->super.boundString, tcp->super.peerString, totalCopied); return (gssize) (totalCopied == 0 ? -1 : totalCopied); }