void Client::Run( void ) { struct UDP_datagram* mBuf_UDP = (struct UDP_datagram*) mBuf; unsigned long currLen = 0; int delay_target = 0; int delay = 0; int adjust = 0; char* readAt = mBuf; #if HAVE_THREAD if ( !isUDP( mSettings ) ) { RunTCP(); return; } #endif // Indicates if the stream is readable bool canRead = true, mMode_Time = isModeTime( mSettings ); // setup termination variables if ( mMode_Time ) { mEndTime.setnow(); mEndTime.add( mSettings->mAmount / 100.0 ); } if ( isUDP( mSettings ) ) { // Due to the UDP timestamps etc, included // reduce the read size by an amount // equal to the header size // compute delay for bandwidth restriction, constrained to [0,1] seconds delay_target = (int) ( mSettings->mBufLen * ((kSecs_to_usecs * kBytes_to_Bits) / mSettings->mUDPRate) ); if ( delay_target < 0 || delay_target > (int) 1 * kSecs_to_usecs ) { fprintf( stderr, warn_delay_large, delay_target / kSecs_to_usecs ); delay_target = (int) kSecs_to_usecs * 1; } if ( isFileInput( mSettings ) ) { if ( isCompat( mSettings ) ) { Extractor_reduceReadSize( sizeof(struct UDP_datagram), mSettings ); readAt += sizeof(struct UDP_datagram); } else { Extractor_reduceReadSize( sizeof(struct UDP_datagram) + sizeof(struct client_hdr), mSettings ); readAt += sizeof(struct UDP_datagram) + sizeof(struct client_hdr); } } } ReportStruct *reportstruct = NULL; // InitReport handles Barrier for multiple Streams mSettings->reporthdr = InitReport( mSettings ); reportstruct = new ReportStruct; reportstruct->packetID = 0; lastPacketTime.setnow(); do { // Test case: drop 17 packets and send 2 out-of-order: // sequence 51, 52, 70, 53, 54, 71, 72 //switch( datagramID ) { // case 53: datagramID = 70; break; // case 71: datagramID = 53; break; // case 55: datagramID = 71; break; // default: break; //} gettimeofday( &(reportstruct->packetTime), NULL ); if ( isUDP( mSettings ) ) { // store datagram ID into buffer mBuf_UDP->id = htonl( (reportstruct->packetID)++ ); mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); // delay between writes // make an adjustment for how long the last loop iteration took // TODO this doesn't work well in certain cases, like 2 parallel streams adjust = delay_target + lastPacketTime.subUsec( reportstruct->packetTime ); lastPacketTime.set( reportstruct->packetTime.tv_sec, reportstruct->packetTime.tv_usec ); if ( adjust > 0 || delay > 0 ) { delay += adjust; } } // Read the next data block from // the file if it's file input if ( isFileInput( mSettings ) ) { Extractor_getNextDataBlock( readAt, mSettings ); canRead = Extractor_canRead( mSettings ) != 0; } else canRead = true; // perform write currLen = write( mSettings->mSock, mBuf, mSettings->mBufLen ); if ( currLen < 0 && errno != ENOBUFS ) { WARN_errno( currLen < 0, "write2" ); break; } // report packets reportstruct->packetLen = currLen; ReportPacket( mSettings->reporthdr, reportstruct ); if ( delay > 0 ) { delay_loop( delay ); } if ( !mMode_Time ) { /* mAmount may be unsigned, so don't let it underflow! */ if( mSettings->mAmount >= currLen ) { mSettings->mAmount -= currLen; } else { mSettings->mAmount = 0; } } } while ( ! (sInterupted || (mMode_Time && mEndTime.before( reportstruct->packetTime )) || (!mMode_Time && 0 >= mSettings->mAmount)) && canRead ); // stop timing gettimeofday( &(reportstruct->packetTime), NULL ); CloseReport( mSettings->reporthdr, reportstruct ); if ( isUDP( mSettings ) ) { // send a final terminating datagram // Don't count in the mTotalLen. The server counts this one, // but didn't count our first datagram, so we're even now. // The negative datagram ID signifies termination to the server. // store datagram ID into buffer mBuf_UDP->id = htonl( -(reportstruct->packetID) ); mBuf_UDP->tv_sec = htonl( reportstruct->packetTime.tv_sec ); mBuf_UDP->tv_usec = htonl( reportstruct->packetTime.tv_usec ); if ( isMulticast( mSettings ) ) { write( mSettings->mSock, mBuf, mSettings->mBufLen ); } else { write_UDP_FIN( ); } } DELETE_PTR( reportstruct ); EndReport( mSettings->reporthdr ); }
void TCPServer::DoTCPThreadInternal(){ s32 ret; s32 len; while (1) { if(exitThread) break; memset(&(this->sock_addr),0,sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_port = DEFAULT_TCP_PORT; sock_addr.sin_addr.s_addr = 0; this->sockfd = ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(ret == -1){ ErrorHandling(); continue;} s32 enable = 1; setsockopt(this->sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); ret = bind(this->sockfd, (sockaddr *)&sock_addr, 16); if(ret < 0) { ErrorHandling(); continue;} ret = listen(this->sockfd, 1); if(ret < 0){ ErrorHandling(); continue;} do{ if(HID_DEBUG){ printf("TCPServer::DoTCPThreadInternal(line %d): Waiting for a connection\n",__LINE__); } if(exitThread) break; len = 16; /** Handshake 1. At first this server sends his protocol version 2. The network clients answers with his preferred version (which needs to be equals or lower the version this server sent him) or an abort command. 3a. If the client sent a abort, close the connection and wait for another connection 3b. If the client sent his highest supported version, the server confirm that he is able to use this version (by sending the version back) or sending a abort command to disconnect. **/ clientfd = ret = (s32)accept(sockfd, (sockaddr *)&(sock_addr),(socklen_t *) &len); if(ret == -1){ ErrorHandling(); break;} printf("TCPServer::DoTCPThreadInternal(line %d): TCP Connection accepted! Sending my protocol version: %d (0x%02X)\n",__LINE__, (WIIU_CP_TCP_HANDSHAKE - WIIU_CP_TCP_HANDSHAKE_VERSION_1)+1,WIIU_CP_TCP_HANDSHAKE); gUDPClientip = sock_addr.sin_addr.s_addr; UDPClient::createInstance(); s32 ret; ret = ControllerPatcherNet::sendbyte(clientfd, WIIU_CP_TCP_HANDSHAKE); //Hey I'm a WiiU console! if(ret < 0){ printf("TCPServer::DoTCPThreadInternal(line %d): Error sendbyte: %02X\n",__LINE__,WIIU_CP_TCP_HANDSHAKE); ErrorHandling(); break;} u8 clientProtocolVersion = ControllerPatcherNet::recvbyte(clientfd); if(ret < 0){ printf("TCPServer::DoTCPThreadInternal(line %d): Error recvbyte: %02X\n",__LINE__,WIIU_CP_TCP_HANDSHAKE); ErrorHandling(); break;} if(clientProtocolVersion == WIIU_CP_TCP_HANDSHAKE_ABORT){ printf("TCPServer::DoTCPThreadInternal(line %d): The network client wants to abort.\n",__LINE__); ErrorHandling(); break; } printf("TCPServer::DoTCPThreadInternal(line %d): received protocol version: %d (0x%02X)\n",__LINE__,(clientProtocolVersion - WIIU_CP_TCP_HANDSHAKE_VERSION_1)+1,clientProtocolVersion); if(clientProtocolVersion >= WIIU_CP_TCP_HANDSHAKE_VERSION_MIN && clientProtocolVersion <= WIIU_CP_TCP_HANDSHAKE_VERSION_MAX){ printf("TCPServer::DoTCPThreadInternal(line %d): We support this protocol version. Let's confirm it to the network client.\n",__LINE__); gUsedProtocolVersion = clientProtocolVersion; ret = ControllerPatcherNet::sendbyte(clientfd, clientProtocolVersion); if(ret < 0){ printf("TCPServer::DoTCPThreadInternal(line %d): Error sendbyte: %02X\n",__LINE__,clientProtocolVersion); ErrorHandling(); break;} }else{ printf("TCPServer::DoTCPThreadInternal(line %d): We don't support this protocol version. We need to abort =(.\n",__LINE__); ret = ControllerPatcherNet::sendbyte(clientfd, WIIU_CP_TCP_HANDSHAKE_ABORT); ErrorHandling(); break; } printf("TCPServer::DoTCPThreadInternal(line %d): Handshake done! Success!\n",__LINE__); TCPServer::DetachAndDelete(); //Clear connected controller RunTCP(); if(clientfd != -1){ socketclose(clientfd); } clientfd = -1; }while(0); printf("TCPServer::DoTCPThreadInternal(line %d): Connection closed\n",__LINE__); gUDPClientip = 0; UDPClient::destroyInstance(); TCPServer::DetachAndDelete(); //Clear connected controller CloseSockets(); continue; } }