# tcp.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00014 #ifdef ENABLE_NETWORK
    00015
    00016 #include "../../stdafx.h"
    00017 #include "../../debug.h"
    00018
    00019 #include "tcp.h"
    00020
    00021 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
    00022     NetworkSocketHandler(),
    00023     packet_queue(NULL), packet_recv(NULL),
    00024     sock(s), writable(false)
    00025 {
    00026 }
    00027
    00028 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
    00029 {
    00030   this->CloseConnection();
    00031
    00032   if (this->sock != INVALID_SOCKET) closesocket(this->sock);
    00033   this->sock = INVALID_SOCKET;
    00034 }
    00035
    00036 NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
    00037 {
    00038   this->writable = false;
    00039   NetworkSocketHandler::CloseConnection(error);
    00040
    00041   /* Free all pending and partially received packets */
    00042   while (this->packet_queue != NULL) {
    00043     Packet *p = this->packet_queue->next;
    00044     delete this->packet_queue;
    00045     this->packet_queue = p;
    00046   }
    00047   delete this->packet_recv;
    00048   this->packet_recv = NULL;
    00049
    00050   return NETWORK_RECV_STATUS_OKAY;
    00051 }
    00052
    00059 void NetworkTCPSocketHandler::SendPacket(Packet *packet)
    00060 {
    00061   Packet *p;
    00062   assert(packet != NULL);
    00063
    00064   packet->PrepareToSend();
    00065
    00066   /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and
    00067    * keeping the other 1400+ bytes wastes memory, especially when someone tries
    00068    * to do a denial of service attack! */
    00069   packet->buffer = ReallocT(packet->buffer, packet->size);
    00070
    00071   /* Locate last packet buffered for the client */
    00072   p = this->packet_queue;
    00073   if (p == NULL) {
    00074     /* No packets yet */
    00075     this->packet_queue = packet;
    00076   } else {
    00077     /* Skip to the last packet */
    00078     while (p->next != NULL) p = p->next;
    00079     p->next = packet;
    00080   }
    00081 }
    00082
    00093 SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
    00094 {
    00095   ssize_t res;
    00096   Packet *p;
    00097
    00098   /* We can not write to this socket!! */
    00099   if (!this->writable) return SPS_NONE_SENT;
    00100   if (!this->IsConnected()) return SPS_CLOSED;
    00101
    00102   p = this->packet_queue;
    00103   while (p != NULL) {
    00104     res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0);
    00105     if (res == -1) {
    00106       int err = GET_LAST_ERROR();
    00107       if (err != EWOULDBLOCK) {
    00108         /* Something went wrong.. close client! */
    00109         if (!closing_down) {
    00110           DEBUG(net, 0, "send failed with error %d", err);
    00111           this->CloseConnection();
    00112         }
    00113         return SPS_CLOSED;
    00114       }
    00115       return SPS_PARTLY_SENT;
    00116     }
    00117     if (res == 0) {
    00118       /* Client/server has left us :( */
    00119       if (!closing_down) this->CloseConnection();
    00120       return SPS_CLOSED;
    00121     }
    00122
    00123     p->pos += res;
    00124
    00125     /* Is this packet sent? */
    00126     if (p->pos == p->size) {
    00127       /* Go to the next packet */
    00128       this->packet_queue = p->next;
    00129       delete p;
    00130       p = this->packet_queue;
    00131     } else {
    00132       return SPS_PARTLY_SENT;
    00133     }
    00134   }
    00135
    00136   return SPS_ALL_SENT;
    00137 }
    00138
    00144 Packet *NetworkTCPSocketHandler::ReceivePacket()
    00145 {
    00146   ssize_t res;
    00147
    00148   if (!this->IsConnected()) return NULL;
    00149
    00150   if (this->packet_recv == NULL) {
    00151     this->packet_recv = new Packet(this);
    00152   }
    00153
    00154   Packet *p = this->packet_recv;
    00155
    00156   /* Read packet size */
    00157   if (p->pos < sizeof(PacketSize)) {
    00158     while (p->pos < sizeof(PacketSize)) {
    00159     /* Read the size of the packet */
    00160       res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
    00161       if (res == -1) {
    00162         int err = GET_LAST_ERROR();
    00163         if (err != EWOULDBLOCK) {
    00164           /* Something went wrong... (104 is connection reset by peer) */
    00165           if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
    00166           this->CloseConnection();
    00167           return NULL;
    00168         }
    00169         /* Connection would block, so stop for now */
    00170         return NULL;
    00171       }
    00172       if (res == 0) {
    00173         /* Client/server has left */
    00174         this->CloseConnection();
    00175         return NULL;
    00176       }
    00177       p->pos += res;
    00178     }
    00179
    00180     /* Read the packet size from the received packet */
    00181     p->ReadRawPacketSize();
    00182
    00183     if (p->size > SEND_MTU) {
    00184       this->CloseConnection();
    00185       return NULL;
    00186     }
    00187   }
    00188
    00189   /* Read rest of packet */
    00190   while (p->pos < p->size) {
    00191     res = recv(this->sock, (char*)p->buffer + p->pos, p->size - p->pos, 0);
    00192     if (res == -1) {
    00193       int err = GET_LAST_ERROR();
    00194       if (err != EWOULDBLOCK) {
    00195         /* Something went wrong... (104 is connection reset by peer) */
    00196         if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
    00197         this->CloseConnection();
    00198         return NULL;
    00199       }
    00200       /* Connection would block */
    00201       return NULL;
    00202     }
    00203     if (res == 0) {
    00204       /* Client/server has left */
    00205       this->CloseConnection();
    00206       return NULL;
    00207     }
    00208
    00209     p->pos += res;
    00210   }
    00211
    00212   /* Prepare for receiving a new packet */
    00213   this->packet_recv = NULL;
    00214
    00215   p->PrepareToRead();
    00216   return p;
    00217 }
    00218
    00224 bool NetworkTCPSocketHandler::CanSendReceive()
    00225 {
    00226   fd_set read_fd, write_fd;
    00227   struct timeval tv;
    00228
    00229   FD_ZERO(&read_fd);
    00230   FD_ZERO(&write_fd);
    00231
    00232   FD_SET(this->sock, &read_fd);
    00233   FD_SET(this->sock, &write_fd);
    00234
    00235   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
    00236 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
    00237   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
    00238 #else
    00239   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
    00240 #endif
    00241
    00242   this->writable = !!FD_ISSET(this->sock, &write_fd);
    00243   return FD_ISSET(this->sock, &read_fd) != 0;
    00244 }
    00245
    00246 #endif /* ENABLE_NETWORK */