int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length) { /* This internal method is called to schedule bytes that * will be sent out to the remote peer. * It's not directly accessed by the caller, who hits ::SendOutboundData, * which may or may not filter or encrypt the caller's data before * sending it here. */ // Highly naive and incomplete implementation. // There's no throttle for runaways (which should abort only this connection // and not the whole process), and no coalescing of small pages. // (Well, not so bad, small pages are coalesced in ::Write) if (IsCloseScheduled()) //if (bCloseNow || bCloseAfterWriting) return 0; if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); char *buffer = (char *) malloc (length + 1); if (!buffer) throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; OutboundPages.push_back (OutboundPage (buffer, length)); OutboundDataSize += length; _UpdateEvents(false, true); return length; }
int PipeDescriptor::SendOutboundData (const char *data, int length) { //if (bCloseNow || bCloseAfterWriting) if (IsCloseScheduled()) return 0; if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); char *buffer = (char *) malloc (length + 1); if (!buffer) throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; OutboundPages.push_back (OutboundPage (buffer, length)); OutboundDataSize += length; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); assert (MyEventMachine); MyEventMachine->Modify (this); #endif return length; }
void DatagramDescriptor::Write() { /* It's possible for a socket to select writable and then no longer * be writable by the time we get around to writing. The kernel might * have used up its available output buffers between the select call * and when we get here. So this condition is not an error. * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData, * but differs in the that the outbound data pages (received from the * user) are _message-structured._ That is, we send each of them out * one message at a time. * TODO, we are currently suppressing the EMSGSIZE error!!! */ int sd = GetSocket(); assert (sd != INVALID_SOCKET); LastIo = gCurrentLoopTime; assert (OutboundPages.size() > 0); // Send out up to 10 packets, then cycle the machine. for (int i = 0; i < 10; i++) { if (OutboundPages.size() <= 0) break; OutboundPage *op = &(OutboundPages[0]); // The nasty cast to (char*) is needed because Windows is brain-dead. int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From)); int e = errno; OutboundDataSize -= op->Length; op->Free(); OutboundPages.pop_front(); if (s == SOCKET_ERROR) { #ifdef OS_UNIX if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) { #endif #ifdef OS_WIN32 if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) { #endif Close(); break; } } } #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); assert (MyEventMachine); MyEventMachine->Modify (this); #endif } /********************************** DatagramDescriptor::SelectForWrite **********************************/ bool DatagramDescriptor::SelectForWrite() { /* Changed 15Nov07, per bug report by Mark Zvillius. * The outbound data size will be zero if there are zero-length outbound packets, * so we now select writable in case the outbound page buffer is not empty. * Note that the superclass ShouldDelete method still checks for outbound data size, * which may be wrong. */ //return (GetOutboundDataSize() > 0); (Original) return (OutboundPages.size() > 0); } /************************************ DatagramDescriptor::SendOutboundData ************************************/ int DatagramDescriptor::SendOutboundData (const char *data, int length) { // This is an exact clone of ConnectionDescriptor::SendOutboundData. // That means it needs to move to a common ancestor. if (IsCloseScheduled()) //if (bCloseNow || bCloseAfterWriting) return 0; if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); char *buffer = (char *) malloc (length + 1); if (!buffer) throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; OutboundPages.push_back (OutboundPage (buffer, length, ReturnAddress)); OutboundDataSize += length; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); assert (MyEventMachine); MyEventMachine->Modify (this); #endif return length; } /**************************************** DatagramDescriptor::SendOutboundDatagram ****************************************/ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port) { // This is an exact clone of ConnectionDescriptor::SendOutboundData. // That means it needs to move to a common ancestor. // TODO: Refactor this so there's no overlap with SendOutboundData. if (IsCloseScheduled()) //if (bCloseNow || bCloseAfterWriting) return 0; if (!address || !*address || !port) return 0; sockaddr_in pin; unsigned long HostAddr; HostAddr = inet_addr (address); if (HostAddr == INADDR_NONE) { // The nasty cast to (char*) is because Windows is brain-dead. hostent *hp = gethostbyname ((char*)address); if (!hp) return 0; HostAddr = ((in_addr*)(hp->h_addr))->s_addr; } memset (&pin, 0, sizeof(pin)); pin.sin_family = AF_INET; pin.sin_addr.s_addr = HostAddr; pin.sin_port = htons (port); if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); char *buffer = (char *) malloc (length + 1); if (!buffer) throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; OutboundPages.push_back (OutboundPage (buffer, length, pin)); OutboundDataSize += length; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); assert (MyEventMachine); MyEventMachine->Modify (this); #endif return length; } /**************************************** STATIC: DatagramDescriptor::SendDatagram ****************************************/ int DatagramDescriptor::SendDatagram (const char *binding, const char *data, int length, const char *address, int port) { DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding)); if (dd) return dd->SendOutboundDatagram (data, length, address, port); else return -1; }