src/proto2/NetworkTCP.cc
branchno-netsession
changeset 31 d0d7489d4e8b
child 32 2ff929186c90
equal deleted inserted replaced
30:0e6f454ecf0e 31:d0d7489d4e8b
       
     1 
       
     2 #include "NetworkTCP.hh"
       
     3 
       
     4 #include <cstdlib>
       
     5 #include <cassert>
       
     6 
       
     7 NetworkBuffer::NetworkBuffer (NetworkSocket &socket, size_t size_hint) :
       
     8     socket(socket), buf(0), size(0), offset(0) {
       
     9     
       
    10     // allocate initial buffer
       
    11     if ((buf = (char *) malloc(size_hint)) == NULL)
       
    12        throw CL_Error("malloc failed");
       
    13     
       
    14     // remember size
       
    15     size = size_hint;
       
    16 }
       
    17 
       
    18 void NetworkBuffer::resize (size_t new_size) {
       
    19     // realloc buffer
       
    20     if ((buf = (char *) realloc((void *) buf, new_size)) == NULL)
       
    21         throw CL_Error("realloc failed");
       
    22     
       
    23     // update size
       
    24     size = new_size;
       
    25 }
       
    26         
       
    27 void NetworkBuffer::write (const char *buf_ptr, size_t buf_size) {
       
    28     int ret;
       
    29 
       
    30     // try and short-circuit writes unless we have already buffered data
       
    31     if (offset == 0) {
       
    32         try {
       
    33             // attempt to send something
       
    34             ret = socket.send(buf_ptr, buf_size);
       
    35 
       
    36         } catch (CL_Error &e) {
       
    37             // ignore EAGAIN, detect this by setting ret to -1
       
    38             if (errno != EAGAIN)
       
    39                 throw;
       
    40 
       
    41             ret = -1;
       
    42         }
       
    43         
       
    44         // if we managed to send something, adjust buf/size and buffer
       
    45         if (ret > 0) {
       
    46             buf_ptr += ret;
       
    47             buf_size -= ret;
       
    48 
       
    49             // sanity-check
       
    50             assert(buf_size >= 0);
       
    51 
       
    52             // if that was all, we're done
       
    53             if (buf_size == 0)
       
    54                 return;
       
    55         }
       
    56     }
       
    57     
       
    58     size_t new_size = size;
       
    59     
       
    60     // calcluate new buffer size
       
    61     while (offset + buf_size > new_size)
       
    62         new_size *= 2;
       
    63     
       
    64     // grow internal buffer if needed
       
    65     if (new_size != size)
       
    66         resize(new_size);
       
    67 
       
    68     // copy into our internal buffer
       
    69     memcpy(buf + offset, buf_ptr, buf_size);
       
    70 }
       
    71         
       
    72 void NetworkBuffer::flush_write (void) {
       
    73     int ret;
       
    74 
       
    75     // ignore if we don't have any data buffered
       
    76     if (offset == 0)
       
    77         return;
       
    78     
       
    79     // attempt to write as much as possible
       
    80     try {
       
    81         ret = socket.send(buf, offset)
       
    82 
       
    83     } catch (CL_Error &e) {
       
    84         // ignore EAGAIN and just return
       
    85         if (errno == EAGAIN)
       
    86             return;
       
    87 
       
    88         else
       
    89             throw;
       
    90     }
       
    91 
       
    92     // update offset
       
    93     offset -= ret;
       
    94 
       
    95     // shift the buffer forwards from (buf + ret) -> (buf), copying (old_offset - ret) bytes
       
    96     memmove(buf, buf + ret, offset);
       
    97 }
       
    98 
       
    99 NetworkTCPTransport::NetworkTCPTransport (CL_Socket socket) :
       
   100     socket(socket), in(NETWORK_TCP_INITIAL_IN_BUF), out(NETWORK_TCP_INITIAL_OUT_BUF) {
       
   101     
       
   102     // use nonblocking sockets
       
   103     socket.set_nonblocking(true);
       
   104 
       
   105     // connect signals
       
   106     slots.connect(socket.sig_read_triggered(), this, &NetworkTCPTransport::on_read);
       
   107     slots.connect(socket.sig_write_triggered(), this, &NetworkTCPTransport::on_write);
       
   108     slots.connect(socket.sig_disconnected_triggered(), this, &NetworkTCPTransport::on_disconnected);
       
   109 }
       
   110 
       
   111 
       
   112 void NetworkTCPTransport::on_read (void) {
       
   113     NetworkPacket packet;
       
   114 
       
   115     do {
       
   116         size_t to_read = 0;
       
   117 
       
   118         // guess how much data to receive based on either the given length prefix or our minimim chunk size
       
   119         if (in.read_prefix<uint16_t>(to_read) == false || to_read < NETWORK_TCP_CHUNK_SIZE)
       
   120             to_read = NETWORK_TCP_CHUNK_SIZE 
       
   121         
       
   122         // do the recv
       
   123         if (in.recv(socket, to_read) == -1)
       
   124         
       
   125         // read out any packets
       
   126         while (in.read_prefix_packet<uint16_t>(packet)) {
       
   127             handle_packet(packet);
       
   128         }
       
   129     } while (...);
       
   130 }
       
   131 
       
   132 void NetworkTCPTransport::on_write (void) {
       
   133     // just flush the output buffer
       
   134     out.flush_write();
       
   135 }
       
   136 
       
   137 void NetworkTCPTransport::on_disconnected (void) {
       
   138 
       
   139 }
       
   140         
       
   141 void NetworkTCPTransport::write_packet (const NetworkPacket &packet) {
       
   142     // just write to the output buffer
       
   143     out.write(packet.get_buf(), packet.get_size());
       
   144 }
       
   145 
       
   146 NetworkTCPServer::NetworkTCPServer (const NetworkAddress &listen_addr) :
       
   147     socket(tcp, ipv4) {
       
   148     
       
   149     // bind
       
   150     socket.bind(listen_addr);
       
   151 
       
   152     // assign slots
       
   153     slots.connect(socket.sig_read_triggered(), this, &NetworkTCPServer::on_accept);
       
   154 
       
   155     // listen
       
   156     socket.listen(NETWORK_LISTEN_BACKLOG);
       
   157 }
       
   158 
       
   159 
       
   160 void NetworkTCPServer::on_accept (void) {
       
   161     // accept a new socket
       
   162     NetworkSocket client_sock = socket.accept();
       
   163 
       
   164     // create a new NetworkTCPTransport
       
   165     NetworkTCPTransport *client = new NetworkTCPTransport(client_sock);
       
   166 
       
   167     // let our user handle it
       
   168     handle_client(client);
       
   169 }
       
   170         
       
   171 NetworkTCPClient::NetworkTCPClient (const NetworkAddress &connect_addr) :
       
   172     NetworkTCPTransport(NetworkSocket(tcp, ipv4)) {
       
   173 
       
   174     // connect
       
   175     socket.connect(connect_addr);
       
   176 
       
   177 }