static void _tcp_endOfFileSignalled(TCP* tcp) { MAGIC_ASSERT(tcp); debug("%s <-> %s: signaling close to user, socket no longer usable", tcp->super.boundString, tcp->super.peerString); tcp->flags |= TCPF_EOF_SIGNALED; /* user can no longer access socket */ descriptor_adjustStatus(&(tcp->super.super.super), DS_CLOSED, TRUE); descriptor_adjustStatus(&(tcp->super.super.super), DS_ACTIVE, FALSE); }
gint tcp_acceptServerPeer(TCP* tcp, in_addr_t* ip, in_port_t* port, gint* acceptedHandle) { MAGIC_ASSERT(tcp); g_assert(acceptedHandle); /* make sure we are listening and bound to an ip and port */ if(tcp->state != TCPS_LISTEN || !(tcp->super.flags & SF_BOUND)) { return EINVAL; } /* we must be a server to accept child connections */ if(tcp->server == NULL){ return EINVAL; } /* if there are no pending connection ready to accept, dont block waiting */ if(g_queue_get_length(tcp->server->pending) <= 0) { return EWOULDBLOCK; } /* double check the pending child before its accepted */ TCPChild* child = g_queue_pop_head(tcp->server->pending); if(!child || (child->tcp && child->tcp->error == TCPE_CONNECTION_RESET)) { return ECONNABORTED; } MAGIC_ASSERT(child); g_assert(child->tcp); /* better have a peer if we are established */ g_assert(child->tcp->super.peerIP && child->tcp->super.peerPort); /* child now gets "accepted" */ child->state = TCPCS_ACCEPTED; /* update child descriptor status */ descriptor_adjustStatus(&(child->tcp->super.super.super), DS_ACTIVE|DS_WRITABLE, TRUE); /* update server descriptor status */ if(g_queue_get_length(tcp->server->pending) > 0) { descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE); } else { descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, FALSE); } *acceptedHandle = child->tcp->super.super.super.handle; if(ip) { *ip = child->tcp->super.peerIP; } if(port) { *port = child->tcp->super.peerPort; } return 0; }
Packet* socket_removeFromOutputBuffer(Socket* socket) { MAGIC_ASSERT(socket); /* see if we have any packets */ Packet* packet = g_queue_pop_head(socket->outputBuffer); if(packet) { /* just removed a packet */ guint length = packet_getPayloadLength(packet); socket->outputBufferLength -= length; /* check if we need to reduce the buffer size */ if(socket->outputBufferSizePending > 0) { socket_setOutputBufferSize(socket, socket->outputBufferSizePending); } /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we are writable if we now have space */ if(socket_getOutputBufferSpace(socket) > 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, TRUE); } } return packet; }
static gssize channel_receiveUserData(Channel* channel, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(channel); /* the write end of a unidirectional pipe can not read! */ g_assert(channel->type != CT_WRITEONLY); gsize available = channel->bufferLength; if(available == 0) { /* we have no data */ if(!channel_getLinkedChannel(channel)) { /* the other end closed (EOF) */ return 0; } else { /* blocking on read */ return -1; } } /* accept some data from the other end of the pipe */ guint copyLength = (guint) MIN(nBytes, available); bytequeue_pop(channel->buffer, buffer, copyLength); channel->bufferLength -= copyLength; /* we are no longer readable if we have nothing left */ if(channel->bufferLength <= 0) { descriptor_adjustStatus((Descriptor*)channel, DS_READABLE, FALSE); } return copyLength; }
gboolean socket_addToInputBuffer(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); /* check if the packet fits */ guint length = packet_getPayloadLength(packet); if(length > socket_getInputBufferSpace(socket)) { return FALSE; } /* add to our queue */ g_queue_push_tail(socket->inputBuffer, packet); packet_ref(packet); socket->inputBufferLength += length; packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_BUFFERED); /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketInputBuffer(tracker, descriptor->handle, socket->inputBufferLength, socket->inputBufferSize); /* we just added a packet, so we are readable */ if(socket->inputBufferLength > 0) { descriptor_adjustStatus((Descriptor*)socket, DS_READABLE, TRUE); } return TRUE; }
gboolean socket_addToOutputBuffer(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); /* check if the packet fits */ guint length = packet_getPayloadLength(packet); if(length > socket_getOutputBufferSpace(socket)) { return FALSE; } /* add to our queue */ g_queue_push_tail(socket->outputBuffer, packet); socket->outputBufferLength += length; packet_addDeliveryStatus(packet, PDS_SND_SOCKET_BUFFERED); /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we just added a packet, we are no longer writable if full */ if(socket_getOutputBufferSpace(socket) <= 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, FALSE); } /* tell the interface to include us when sending out to the network */ in_addr_t ip = packet_getSourceIP(packet); NetworkInterface* interface = host_lookupInterface(worker_getCurrentHost(), ip); networkinterface_wantsSend(interface, socket); return TRUE; }
Channel* channel_new(gint handle, gint linkedHandle, enum ChannelType type) { Channel* channel = g_new0(Channel, 1); MAGIC_INIT(channel); transport_init(&(channel->super), &channel_functions, DT_PIPE, handle); channel->type = type; channel->buffer = bytequeue_new(8192); channel->bufferSize = CONFIG_PIPE_BUFFER_SIZE; channel->linkedHandle = linkedHandle; descriptor_adjustStatus((Descriptor*)channel, DS_ACTIVE, TRUE); if(!(type & CT_READONLY)) { descriptor_adjustStatus((Descriptor*)channel, DS_WRITABLE, TRUE); } return channel; }
static gsize _tcp_getBufferSpaceOut(TCP* tcp) { MAGIC_ASSERT(tcp); /* account for throttled and retransmission buffer */ gssize s = (gssize)(socket_getOutputBufferSpace(&(tcp->super)) - tcp->throttledOutputLength - tcp->retransmissionLength); gsize space = MAX(0, s); if(space == 0) { descriptor_adjustStatus((Descriptor*)tcp, DS_WRITABLE, FALSE); } return space; }
UDP* udp_new(gint handle, guint receiveBufferSize, guint sendBufferSize) { UDP* udp = g_new0(UDP, 1); MAGIC_INIT(udp); socket_init(&(udp->super), &udp_functions, DT_UDPSOCKET, handle, receiveBufferSize, sendBufferSize); /* we are immediately active because UDP doesnt wait for accept or connect */ descriptor_adjustStatus((Descriptor*) udp, DS_ACTIVE|DS_WRITABLE, TRUE); return udp; }
static gssize channel_sendUserData(Channel* channel, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(channel); /* the read end of a unidirectional pipe can not write! */ g_assert(channel->type != CT_READONLY); gint result = 0; Channel* linkedChannel = channel_getLinkedChannel(channel); if(linkedChannel) { result = channel_linkedWrite(linkedChannel, buffer, nBytes); } /* our end cant write anymore if they returned error */ if(result <= 0) { descriptor_adjustStatus((Descriptor*)channel, DS_WRITABLE, FALSE); } return result; }
static gssize channel_linkedWrite(Channel* channel, gconstpointer buffer, gsize nBytes) { MAGIC_ASSERT(channel); /* our linked channel is trying to send us data, make sure we can read it */ g_assert(!(channel->type & CT_WRITEONLY)); gsize available = channel->bufferSize - channel->bufferLength; if(available == 0) { /* we have no space */ return -1; } /* accept some data from the other end of the pipe */ guint copyLength = (guint) MIN(nBytes, available); bytequeue_push(channel->buffer, buffer, copyLength); channel->bufferLength += copyLength; /* we just got some data in our buffer */ descriptor_adjustStatus((Descriptor*)channel, DS_READABLE, TRUE); return copyLength; }
/* return TRUE if the packet should be retransmitted */ gboolean tcp_processPacket(TCP* tcp, Packet* packet) { MAGIC_ASSERT(tcp); /* fetch the TCP info from the packet */ PacketTCPHeader header; packet_getTCPHeader(packet, &header); guint packetLength = packet_getPayloadLength(packet); /* if we run a server, the packet could be for an existing child */ tcp = _tcp_getSourceTCP(tcp, header.sourceIP, header.sourcePort); /* now we have the true TCP for the packet */ MAGIC_ASSERT(tcp); /* print packet info for debugging */ debug("%s <-> %s: processing packet# %u length %u", tcp->super.boundString, tcp->super.peerString, header.sequence, packetLength); /* if packet is reset, don't process */ if(header.flags & PTCP_RST) { /* @todo: not sure if this is handled correctly */ debug("received RESET packet"); if(!(tcp->state & TCPS_LISTEN) && !(tcp->error & TCPE_CONNECTION_RESET)) { tcp->error |= TCPE_CONNECTION_RESET; tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_TIMEWAIT); /* it will send no more user data after what we have now */ tcp->receive.end = tcp->receive.next; } packet_unref(packet); return FALSE; } /* if we are a server, we have to remember who we got this from so we can * respond back to them. this is because we could be bound to several * interfaces and otherwise cant decide which to send on. */ if(tcp->server) { tcp->server->lastPeerIP = header.sourceIP; tcp->server->lastPeerPort = header.sourcePort; tcp->server->lastIP = header.destinationIP; } /* go through the state machine, tracking processing and response */ gboolean wasProcessed = FALSE; enum ProtocolTCPFlags responseFlags = PTCP_NONE; switch(tcp->state) { case TCPS_LISTEN: { /* receive SYN, send SYNACK, move to SYNRECEIVED */ if(header.flags & PTCP_SYN) { MAGIC_ASSERT(tcp->server); wasProcessed = TRUE; /* we need to multiplex a new child */ Node* node = worker_getPrivate()->cached_node; gint multiplexedHandle = node_createDescriptor(node, DT_TCPSOCKET); TCP* multiplexed = (TCP*) node_lookupDescriptor(node, multiplexedHandle); multiplexed->child = _tcpchild_new(multiplexed, tcp, header.sourceIP, header.sourcePort); g_assert(g_hash_table_lookup(tcp->server->children, &(multiplexed->child->key)) == NULL); g_hash_table_replace(tcp->server->children, &(multiplexed->child->key), multiplexed->child); multiplexed->receive.start = header.sequence; multiplexed->receive.next = multiplexed->receive.start + 1; debug("%s <-> %s: server multiplexed child socket %s <-> %s", tcp->super.boundString, tcp->super.peerString, multiplexed->super.boundString, multiplexed->super.peerString); _tcp_setState(multiplexed, TCPS_SYNRECEIVED); /* parent will send response */ responseFlags = PTCP_SYN|PTCP_ACK; } break; } case TCPS_SYNSENT: { /* receive SYNACK, send ACK, move to ESTABLISHED */ if((header.flags & PTCP_SYN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; tcp->receive.start = header.sequence; tcp->receive.next = tcp->receive.start + 1; responseFlags |= PTCP_ACK; _tcp_setState(tcp, TCPS_ESTABLISHED); } /* receive SYN, send ACK, move to SYNRECEIVED (simultaneous open) */ else if(header.flags & PTCP_SYN) { wasProcessed = TRUE; tcp->receive.start = header.sequence; tcp->receive.next = tcp->receive.start + 1; responseFlags |= PTCP_ACK; _tcp_setState(tcp, TCPS_SYNRECEIVED); } break; } case TCPS_SYNRECEIVED: { /* receive ACK, move to ESTABLISHED */ if(header.flags & PTCP_ACK) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_ESTABLISHED); /* if this is a child, mark it accordingly */ if(tcp->child) { tcp->child->state = TCPCS_PENDING; g_queue_push_tail(tcp->child->parent->server->pending, tcp->child); /* user should accept new child from parent */ descriptor_adjustStatus(&(tcp->child->parent->super.super.super), DS_READABLE, TRUE); } } break; } case TCPS_ESTABLISHED: { /* receive FIN, send FINACK, move to CLOSEWAIT */ if(header.flags & PTCP_FIN) { wasProcessed = TRUE; /* other side of connections closed */ tcp->flags |= TCPF_REMOTE_CLOSED; responseFlags |= (PTCP_FIN|PTCP_ACK); _tcp_setState(tcp, TCPS_CLOSEWAIT); /* remote will send us no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_FINWAIT1: { /* receive FINACK, move to FINWAIT2 */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_FINWAIT2); } /* receive FIN, send FINACK, move to CLOSING (simultaneous close) */ else if(header.flags & PTCP_FIN) { wasProcessed = TRUE; responseFlags |= (PTCP_FIN|PTCP_ACK); tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_CLOSING); /* it will send no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_FINWAIT2: { /* receive FIN, send FINACK, move to TIMEWAIT */ if(header.flags & PTCP_FIN) { wasProcessed = TRUE; responseFlags |= (PTCP_FIN|PTCP_ACK); tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_TIMEWAIT); /* it will send no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_CLOSING: { /* receive FINACK, move to TIMEWAIT */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_TIMEWAIT); } break; } case TCPS_TIMEWAIT: { break; } case TCPS_CLOSEWAIT: { break; } case TCPS_LASTACK: { /* receive FINACK, move to CLOSED */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_CLOSED); /* we closed, cant use tcp anymore, no retransmit */ packet_unref(packet); return FALSE; } break; } case TCPS_CLOSED: { /* stray packet, drop without retransmit */ packet_unref(packet); return FALSE; break; } default: { break; } } gint nPacketsAcked = 0; /* check if we can update some TCP control info */ if(header.flags & PTCP_ACK) { wasProcessed = TRUE; if((header.acknowledgement > tcp->send.unacked) && (header.acknowledgement <= tcp->send.next)) { /* some data we sent got acknowledged */ nPacketsAcked = header.acknowledgement - tcp->send.unacked; /* the packets just acked are 'released' from retransmit queue */ for(guint i = tcp->send.unacked; i < header.acknowledgement; i++) { _tcp_removeRetransmit(tcp, i); } tcp->send.unacked = header.acknowledgement; /* update congestion window and keep track of when it was updated */ tcp->congestion.lastWindow = header.window; tcp->congestion.lastSequence = header.sequence; tcp->congestion.lastAcknowledgement = header.acknowledgement; } } gboolean doRetransmitData = FALSE; /* check if the packet carries user data for us */ if(packetLength > 0) { /* it has data, check if its in the correct range */ if(header.sequence >= (tcp->receive.next + tcp->receive.window)) { /* its too far ahead to accept now, but they should re-send it */ wasProcessed = TRUE; doRetransmitData = TRUE; } else if(header.sequence >= tcp->receive.next) { /* its in our window, so we can accept the data */ wasProcessed = TRUE; /* * if this is THE next packet, we MUST accept it to avoid * deadlocks (unless we are blocked b/c user should read) */ gboolean isNextPacket = (header.sequence == tcp->receive.next) ? TRUE : FALSE; gboolean waitingUserRead = (socket_getInputBufferSpace(&(tcp->super)) > 0) ? TRUE : FALSE; gboolean packetFits = (packetLength <= _tcp_getBufferSpaceIn(tcp)) ? TRUE : FALSE; if((isNextPacket && !waitingUserRead) || (packetFits)) { /* make sure its in order */ _tcp_bufferPacketIn(tcp, packet); } else { debug("no space for packet even though its in our window"); doRetransmitData = TRUE; } } } /* if it is a spurious packet, send a reset */ if(!wasProcessed) { g_assert(responseFlags == PTCP_NONE); responseFlags = PTCP_RST; } /* try to update congestion window based on potentially new info */ _tcp_updateCongestionWindow(tcp, nPacketsAcked); /* now flush as many packets as we can to socket */ _tcp_flush(tcp); /* send ack if they need updates but we didn't send any yet (selective acks) */ if((tcp->receive.next > tcp->send.lastAcknowledgement) || (tcp->receive.window != tcp->send.lastWindow)) { responseFlags |= PTCP_ACK; } /* send control packet if we have one */ if(responseFlags != PTCP_NONE) { debug("%s <-> %s: sending response control packet", tcp->super.boundString, tcp->super.peerString); Packet* response = _tcp_createPacket(tcp, responseFlags, NULL, 0); _tcp_bufferPacketOut(tcp, response); _tcp_flush(tcp); } /* we should free packets that are done but were not buffered */ if(!doRetransmitData && packetLength <= 0) { packet_unref(packet); } return doRetransmitData; }
void descriptor_close(Descriptor* descriptor) { MAGIC_ASSERT(descriptor); MAGIC_ASSERT(descriptor->funcTable); descriptor_adjustStatus(descriptor, DS_CLOSED, TRUE); descriptor->funcTable->close(descriptor); }
static void _tcp_flush(TCP* tcp) { MAGIC_ASSERT(tcp); /* make sure our information is up to date */ _tcp_updateReceiveWindow(tcp); _tcp_updateSendWindow(tcp); /* flush packets that can now be sent to socket */ while(g_queue_get_length(tcp->throttledOutput) > 0) { /* get the next throttled packet, in sequence order */ Packet* packet = g_queue_pop_head(tcp->throttledOutput); /* break out if we have no packets left */ if(!packet) { break; } guint length = packet_getPayloadLength(packet); if(length > 0) { PacketTCPHeader header; packet_getTCPHeader(packet, &header); /* we cant send it if our window is too small */ gboolean fitsInWindow = (header.sequence < (tcp->send.unacked + tcp->send.window)) ? TRUE : FALSE; /* we cant send it if we dont have enough space */ gboolean fitsInBuffer = (length <= socket_getOutputBufferSpace(&(tcp->super))) ? TRUE : FALSE; if(!fitsInBuffer || !fitsInWindow) { /* we cant send the packet yet */ g_queue_push_head(tcp->throttledOutput, packet); break; } else { /* we will send: store length in virtual retransmission buffer * so we can reduce buffer space consumed when we receive the ack */ _tcp_addRetransmit(tcp, header.sequence, length); } } /* packet is sendable, we removed it from out buffer */ tcp->throttledOutputLength -= length; /* update TCP header to our current advertised window and acknowledgement */ packet_updateTCP(packet, tcp->receive.next, tcp->receive.window); /* keep track of the last things we sent them */ tcp->send.lastAcknowledgement = tcp->receive.next; tcp->send.lastWindow = tcp->receive.window; /* socket will queue it ASAP */ gboolean success = socket_addToOutputBuffer(&(tcp->super), packet); /* we already checked for space, so this should always succeed */ g_assert(success); } /* any packets now in order can be pushed to our user input buffer */ while(g_queue_get_length(tcp->unorderedInput) > 0) { Packet* packet = g_queue_pop_head(tcp->unorderedInput); PacketTCPHeader header; packet_getTCPHeader(packet, &header); if(header.sequence == tcp->receive.next) { /* move from the unordered buffer to user input buffer */ gboolean fitInBuffer = socket_addToInputBuffer(&(tcp->super), packet); if(fitInBuffer) { tcp->unorderedInputLength -= packet_getPayloadLength(packet); (tcp->receive.next)++; continue; } } /* we could not buffer it because its out of order or we have no space */ g_queue_push_head(tcp->unorderedInput, packet); break; } /* check if user needs an EOF signal */ gboolean wantsEOF = ((tcp->flags & TCPF_LOCAL_CLOSED) || (tcp->flags & TCPF_REMOTE_CLOSED)) ? TRUE : FALSE; if(wantsEOF) { /* if anyone closed, can't send anymore */ tcp->error |= TCPE_SEND_EOF; if((tcp->receive.next >= tcp->receive.end) && !(tcp->flags & TCPF_EOF_SIGNALED)) { /* user needs to read a 0 so it knows we closed */ tcp->error |= TCPE_RECEIVE_EOF; descriptor_adjustStatus((Descriptor*)tcp, DS_READABLE, TRUE); } } }
static void _tcp_setState(TCP* tcp, enum TCPState state) { MAGIC_ASSERT(tcp); tcp->stateLast = tcp->state; tcp->state = state; debug("%s <-> %s: moved from TCP state '%s' to '%s'", tcp->super.boundString, tcp->super.peerString, tcp_stateToAscii(tcp->stateLast), tcp_stateToAscii(tcp->state)); /* some state transitions require us to update the descriptor status */ switch (state) { case TCPS_LISTEN: { descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, TRUE); break; } case TCPS_SYNSENT: { break; } case TCPS_SYNRECEIVED: { break; } case TCPS_ESTABLISHED: { tcp->flags |= TCPF_WAS_ESTABLISHED; if(tcp->state != tcp->stateLast) { _tcp_autotune(tcp); } descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE|DS_WRITABLE, TRUE); break; } case TCPS_CLOSING: { break; } case TCPS_CLOSEWAIT: { break; } case TCPS_CLOSED: { /* user can no longer use socket */ descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, FALSE); /* * servers have to wait for all children to close. * children need to notify their parents when closing. */ if(!tcp->server || g_hash_table_size(tcp->server->children) <= 0) { if(tcp->child && tcp->child->parent) { TCP* parent = tcp->child->parent; /* tell my server to stop accepting packets for me * this will destroy the child and NULL out tcp->child */ g_hash_table_remove(tcp->child->parent->server->children, (gconstpointer)&(tcp->child->key)); /* if i was the server's last child and its waiting to close, close it */ g_assert(parent->server); if((parent->state == TCPS_CLOSED) && (g_hash_table_size(parent->server->children) <= 0)) { /* this will unbind from the network interface and free socket */ node_closeDescriptor(worker_getPrivate()->cached_node, parent->super.super.super.handle); } } /* this will unbind from the network interface and free socket */ node_closeDescriptor(worker_getPrivate()->cached_node, tcp->super.super.super.handle); } break; } case TCPS_TIMEWAIT: { /* schedule a close timer self-event to finish out the closing process */ TCPCloseTimerExpiredEvent* event = tcpclosetimerexpired_new(tcp); worker_scheduleEvent((Event*)event, CONFIG_TCPCLOSETIMER_DELAY, 0); break; } default: break; } }
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); }
static void _epoll_close(Epoll* epoll) { MAGIC_ASSERT(epoll); descriptor_adjustStatus(&(epoll->super), DS_CLOSED, TRUE); node_closeDescriptor(worker_getPrivate()->cached_node, epoll->super.handle); }
static void channel_close(Channel* channel) { MAGIC_ASSERT(channel); descriptor_adjustStatus((Descriptor*)channel, DS_CLOSED, TRUE); node_closeDescriptor(worker_getPrivate()->cached_node, channel->super.super.handle); }