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 */