# tcp.cpp

Go to the documentation of this file.

    00001 /* $Id: tcp.cpp 22403 2011-05-01 13:29:40Z rubidium $ */
    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
    00025 NetworkTCPSocketHandler::NetworkTCPSocketHandler(SOCKET s) :
    00026     NetworkSocketHandler(),
    00027     packet_queue(NULL), packet_recv(NULL),
    00028     sock(s), writable(false)
    00029 {
    00030 }
    00031
    00032 NetworkTCPSocketHandler::~NetworkTCPSocketHandler()
    00033 {
    00034   this->CloseConnection();
    00035
    00036   if (this->sock != INVALID_SOCKET) closesocket(this->sock);
    00037   this->sock = INVALID_SOCKET;
    00038 }
    00039
    00040 NetworkRecvStatus NetworkTCPSocketHandler::CloseConnection(bool error)
    00041 {
    00042   this->writable = false;
    00043   NetworkSocketHandler::CloseConnection(error);
    00044
    00045   /* Free all pending and partially received packets */
    00046   while (this->packet_queue != NULL) {
    00047     Packet *p = this->packet_queue->next;
    00048     delete this->packet_queue;
    00049     this->packet_queue = p;
    00050   }
    00051   delete this->packet_recv;
    00052   this->packet_recv = NULL;
    00053
    00054   return NETWORK_RECV_STATUS_OKAY;
    00055 }
    00056
    00063 void NetworkTCPSocketHandler::SendPacket(Packet *packet)
    00064 {
    00065   Packet *p;
    00066   assert(packet != NULL);
    00067
    00068   packet->PrepareToSend();
    00069
    00070   /* Reallocate the packet as in 99+% of the times we send at most 25 bytes and
    00071    * keeping the other 1400+ bytes wastes memory, especially when someone tries
    00072    * to do a denial of service attack! */
    00073   packet->buffer = ReallocT(packet->buffer, packet->size);
    00074
    00075   /* Locate last packet buffered for the client */
    00076   p = this->packet_queue;
    00077   if (p == NULL) {
    00078     /* No packets yet */
    00079     this->packet_queue = packet;
    00080   } else {
    00081     /* Skip to the last packet */
    00082     while (p->next != NULL) p = p->next;
    00083     p->next = packet;
    00084   }
    00085 }
    00086
    00097 SendPacketsState NetworkTCPSocketHandler::SendPackets(bool closing_down)
    00098 {
    00099   ssize_t res;
    00100   Packet *p;
    00101
    00102   /* We can not write to this socket!! */
    00103   if (!this->writable) return SPS_NONE_SENT;
    00104   if (!this->IsConnected()) return SPS_CLOSED;
    00105
    00106   p = this->packet_queue;
    00107   while (p != NULL) {
    00108     res = send(this->sock, (const char*)p->buffer + p->pos, p->size - p->pos, 0);
    00109     if (res == -1) {
    00110       int err = GET_LAST_ERROR();
    00111       if (err != EWOULDBLOCK) {
    00112         /* Something went wrong.. close client! */
    00113         if (!closing_down) {
    00114           DEBUG(net, 0, "send failed with error %d", err);
    00115           this->CloseConnection();
    00116         }
    00117         return SPS_CLOSED;
    00118       }
    00119       return SPS_PARTLY_SENT;
    00120     }
    00121     if (res == 0) {
    00122       /* Client/server has left us :( */
    00123       if (!closing_down) this->CloseConnection();
    00124       return SPS_CLOSED;
    00125     }
    00126
    00127     p->pos += res;
    00128
    00129     /* Is this packet sent? */
    00130     if (p->pos == p->size) {
    00131       /* Go to the next packet */
    00132       this->packet_queue = p->next;
    00133       delete p;
    00134       p = this->packet_queue;
    00135     } else {
    00136       return SPS_PARTLY_SENT;
    00137     }
    00138   }
    00139
    00140   return SPS_ALL_SENT;
    00141 }
    00142
    00147 Packet *NetworkTCPSocketHandler::ReceivePacket()
    00148 {
    00149   ssize_t res;
    00150
    00151   if (!this->IsConnected()) return NULL;
    00152
    00153   if (this->packet_recv == NULL) {
    00154     this->packet_recv = new Packet(this);
    00155   }
    00156
    00157   Packet *p = this->packet_recv;
    00158
    00159   /* Read packet size */
    00160   if (p->pos < sizeof(PacketSize)) {
    00161     while (p->pos < sizeof(PacketSize)) {
    00162     /* Read the size of the packet */
    00163       res = recv(this->sock, (char*)p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
    00164       if (res == -1) {
    00165         int err = GET_LAST_ERROR();
    00166         if (err != EWOULDBLOCK) {
    00167           /* Something went wrong... (104 is connection reset by peer) */
    00168           if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
    00169           this->CloseConnection();
    00170           return NULL;
    00171         }
    00172         /* Connection would block, so stop for now */
    00173         return NULL;
    00174       }
    00175       if (res == 0) {
    00176         /* Client/server has left */
    00177         this->CloseConnection();
    00178         return NULL;
    00179       }
    00180       p->pos += res;
    00181     }
    00182
    00183     /* Read the packet size from the received packet */
    00184     p->ReadRawPacketSize();
    00185
    00186     if (p->size > SEND_MTU) {
    00187       this->CloseConnection();
    00188       return NULL;
    00189     }
    00190   }
    00191
    00192   /* Read rest of packet */
    00193   while (p->pos < p->size) {
    00194     res = recv(this->sock, (char*)p->buffer + p->pos, p->size - p->pos, 0);
    00195     if (res == -1) {
    00196       int err = GET_LAST_ERROR();
    00197       if (err != EWOULDBLOCK) {
    00198         /* Something went wrong... (104 is connection reset by peer) */
    00199         if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
    00200         this->CloseConnection();
    00201         return NULL;
    00202       }
    00203       /* Connection would block */
    00204       return NULL;
    00205     }
    00206     if (res == 0) {
    00207       /* Client/server has left */
    00208       this->CloseConnection();
    00209       return NULL;
    00210     }
    00211
    00212     p->pos += res;
    00213   }
    00214
    00215   /* Prepare for receiving a new packet */
    00216   this->packet_recv = NULL;
    00217
    00218   p->PrepareToRead();
    00219   return p;
    00220 }
    00221
    00227 bool NetworkTCPSocketHandler::CanSendReceive()
    00228 {
    00229   fd_set read_fd, write_fd;
    00230   struct timeval tv;
    00231
    00232   FD_ZERO(&read_fd);
    00233   FD_ZERO(&write_fd);
    00234
    00235   FD_SET(this->sock, &read_fd);
    00236   FD_SET(this->sock, &write_fd);
    00237
    00238   tv.tv_sec = tv.tv_usec = 0; // don't block at all.
    00239 #if !defined(__MORPHOS__) && !defined(__AMIGA__)
    00240   select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
    00241 #else
    00242   WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
    00243 #endif
    00244
    00245   this->writable = !!FD_ISSET(this->sock, &write_fd);
    00246   return FD_ISSET(this->sock, &read_fd) != 0;
    00247 }
    00248
    00249 #endif /* ENABLE_NETWORK */