udpconnection.cpp

来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 359 行

CPP
359
字号
#include "UDPConnection.h"

#include <SDL_timer.h>
#include <boost/version.hpp>

#ifdef _WIN32
 #include "Platform/Win/win32.h"
#else
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netdb.h>
#endif

#include "ProtocolDef.h"
#include "Exception.h"

namespace netcode {


#ifdef _WIN32
#else
	typedef struct hostent* LPHOSTENT;
	typedef struct in_addr* LPIN_ADDR;
	const int SOCKET_ERROR = -1;
#endif

const unsigned UDPConnection::hsize = 9;

UDPConnection::UDPConnection(boost::shared_ptr<UDPSocket> NetSocket, const sockaddr_in& MyAddr) : mySocket(NetSocket)
{
	addr=MyAddr;
	Init();
}

UDPConnection::UDPConnection(boost::shared_ptr<UDPSocket> NetSocket, const std::string& address, const unsigned port) : mySocket(NetSocket)
{
	LPHOSTENT lpHostEntry;

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);

#ifdef _WIN32
	unsigned long ul;
	if((ul=inet_addr(address.c_str()))!=INADDR_NONE){
		addr.sin_addr.S_un.S_addr = 	ul;
	} else
#else
		if(inet_aton(address.c_str(),&(addr.sin_addr))==0)
#endif
		{
			lpHostEntry = gethostbyname(address.c_str());
			if (lpHostEntry == NULL)
			{
				throw network_error("Error looking up server from DNS: "+address);
			}
			addr.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list);
		}
	Init();
}

UDPConnection::~UDPConnection()
{
	if (fragmentBuffer)
		delete fragmentBuffer;

	while (!msgQueue.empty())
	{
		delete msgQueue.front();
		msgQueue.pop_front();
	}
}

void UDPConnection::SendData(const unsigned char *data, const unsigned length)
{
	if(outgoingLength+length>=NETWORK_BUFFER_SIZE){
		throw network_error("Buffer overflow in UDPConnection (SendData)");
	}
	memcpy(&outgoingData[outgoingLength],data,length);
	outgoingLength+=length;
}

const RawPacket* UDPConnection::Peek(unsigned ahead) const
{
	if (ahead < msgQueue.size())
		return msgQueue[ahead];

	return NULL;
}

RawPacket* UDPConnection::GetData()
{
	if (!msgQueue.empty())
	{
		RawPacket* msg = msgQueue.front();
		msgQueue.pop_front();
		return msg;
	}
	else
		return NULL;
}

void UDPConnection::Update()
{
	const unsigned curTime = SDL_GetTicks();
	bool force = false;	// should we force to send a packet?

	if((dataRecv == 0) && lastSendTime < curTime-1000 && !unackedPackets.empty()){		//server hasnt responded so try to send the connection attempt again
		SendRawPacket(unackedPackets[0].data,unackedPackets[0].length,0);
		lastSendTime = curTime;
		force = true;
	}

	if (lastSendTime<curTime-5000 && !(dataRecv == 0)) { //we havent sent anything for a while so send something to prevent timeout
		force = true;
	}
	else if(lastSendTime<curTime-200 && !waitingPackets.empty()){	//we have at least one missing incomming packet lying around so send a packet to ensure the other side get a nak
		force = true;
	}

	Flush(force);
}

void UDPConnection::ProcessRawPacket(RawPacket* packet)
{
	lastReceiveTime=SDL_GetTicks();
	dataRecv += packet->length;
	recvOverhead += hsize;

	int packetNum = *(int*)packet->data;
	int ack = *(int*)(packet->data+4);
	unsigned char nak = packet->data[8];

	AckPackets(ack);

	if (nak > 0)	// we have lost $nak packets
	{
		int nak_abs = nak + firstUnacked - 1;
		if (nak_abs!=lastNak || lastNakTime < lastReceiveTime-100)
		{
			// resend all packets from firstUnacked till nak_abs
			lastNak=nak_abs;
			lastNakTime=lastReceiveTime;
			for(int b=firstUnacked;b<=nak_abs;++b){
				SendRawPacket(unackedPackets[b-firstUnacked].data,unackedPackets[b-firstUnacked].length,b);
				++resentPackets;
			}
		}
	}

	if(lastInOrder>=packetNum || waitingPackets.find(packetNum)!=waitingPackets.end())
	{
		delete packet;
		return;
	}

	waitingPackets.insert(packetNum, new RawPacket(packet->data + hsize, packet->length - hsize));
	delete packet;
	packet = NULL;

	packetMap::iterator wpi;
	//process all in order packets that we have waiting
	while ((wpi = waitingPackets.find(lastInOrder+1)) != waitingPackets.end())
	{
		unsigned char buf[8000];
		unsigned bufLength = 0;

		if (fragmentBuffer)
		{
			// combine with fragment buffer
			bufLength += fragmentBuffer->length;
			memcpy(buf, fragmentBuffer->data, bufLength);
			delete fragmentBuffer;
			fragmentBuffer = NULL;
		}

		lastInOrder++;
#if (BOOST_VERSION >= 103400)
		memcpy(buf + bufLength, wpi->second->data, wpi->second->length);
		bufLength += (wpi->second)->length;
#else
		memcpy(buf + bufLength, (*wpi).data, (*wpi).length);
		bufLength += (*wpi).length;
#endif
		waitingPackets.erase(wpi);

		for (unsigned pos = 0; pos < bufLength;)
		{
			char msgid = buf[pos];
			ProtocolDef* proto = ProtocolDef::instance();
			if (proto->IsAllowed(msgid))
			{
				unsigned msglength = 0;
				if (proto->HasFixedLength(msgid))
				{
					msglength = proto->GetLength(msgid);
				}
				else
				{
					int length_t = proto->GetLength(msgid);

					// got enough data in the buffer to read the length of the message?
					if (bufLength >= pos + -length_t)
					{
						// yes => read the length (as byte or word)
						if (length_t == -1)
						{
							msglength = buf[pos+1];
						}
						else if (length_t == -2)
						{
							msglength = *(short*)(buf+pos+1);
						}
					}
					else
					{
						// no => store the fragment and break
						fragmentBuffer = new RawPacket(buf + pos, bufLength - pos);
						break;
					}
				}

				// if this isn't true we'll loop infinitely while filling up memory
				assert(msglength != 0);

				// got the complete message in the buffer?
				if (bufLength >= pos + msglength)
				{
					// yes => add to msgQueue and keep going
					msgQueue.push_back(new RawPacket(buf + pos, msglength));
					pos += msglength;
				}
				else
				{
					// no => store the fragment and break
					fragmentBuffer = new RawPacket(buf + pos, bufLength - pos);
					break;
				}
			}
			else
			{
				// error
				pos++;
			}
		}
	}
}

void UDPConnection::Flush(const bool forced)
{
	const float curTime = SDL_GetTicks();
	if (forced || (outgoingLength>0 && (lastSendTime < (curTime - 200 + outgoingLength * 10))))
	{
		ProtocolDef* proto = ProtocolDef::instance();
		lastSendTime=SDL_GetTicks();

		// Manually fragment packets to respect configured UDP_MTU.
		// This is an attempt to fix the bug where players drop out of the game if
		// someone in the game gives a large order.

		if (outgoingLength > proto->UDP_MTU)
			++fragmentedFlushes;

		unsigned pos = 0;
		do
		{
			unsigned length = std::min(proto->UDP_MTU, outgoingLength);
			SendRawPacket(outgoingData + pos, length, currentNum++);
			unackedPackets.push_back(new RawPacket(outgoingData + pos, length));
			outgoingLength -= length;
			pos += proto->UDP_MTU;
		} while (outgoingLength != 0);
	}
}

bool UDPConnection::CheckTimeout() const
{
	const unsigned curTime = SDL_GetTicks();
	const unsigned timeout = ((dataRecv == 0) ? 45000 : 30000);
	if((lastReceiveTime+timeout) < curTime)
	{
		return true;
	}
	else
		return false;
}

bool UDPConnection::CheckAddress(const sockaddr_in& from) const
{
	if(
#ifdef _WIN32
		addr.sin_addr.S_un.S_addr==from.sin_addr.S_un.S_addr
#else
		addr.sin_addr.s_addr==from.sin_addr.s_addr
#endif
		&& addr.sin_port==from.sin_port){
		return true;
	}
	else
		return false;
}

void UDPConnection::Init()
{
	lastReceiveTime = SDL_GetTicks();
	lastInOrder=-1;
	waitingPackets.clear();
	firstUnacked=0;
	currentNum=0;
	outgoingLength=0;
	lastNak=-1;
	lastNakTime=0;
	lastSendTime=0;
	sentOverhead = 0;
	recvOverhead = 0;
	fragmentedFlushes = 0;
	fragmentBuffer = 0;
	resentPackets = 0;
}

void UDPConnection::AckPackets(const int nextAck)
{
	while(nextAck>=firstUnacked && !unackedPackets.empty()){
		unackedPackets.pop_front();
		firstUnacked++;
	}
}

void UDPConnection::SendRawPacket(const unsigned char* data, const unsigned length, const int packetNum)
{
	unsigned char* tempbuf = new unsigned char[hsize+length];
	*(int*)tempbuf = packetNum;
	*(int*)(tempbuf+4) = lastInOrder;
	if(!waitingPackets.empty() && waitingPackets.find(lastInOrder+1)==waitingPackets.end()){
#if (BOOST_VERSION >= 103400)
		int nak = (waitingPackets.begin()->first - 1) - lastInOrder;
#else
		int nak = (waitingPackets.begin().key() - 1) - lastInOrder;
#endif
		assert(nak >= 0);
		if (nak <= 255)
			*(unsigned char*)(tempbuf+8) = (unsigned char)nak;
		else
			*(unsigned char*)(tempbuf+8) = 255;
	}
	else {
		*(unsigned char*)(tempbuf+8) = 0;
	}

	memcpy(tempbuf+hsize, data, length);
	mySocket->SendTo(tempbuf, length+hsize, &addr);
	delete[] tempbuf;

	dataSent += length;
	sentOverhead += hsize;
}


} // namespace netcode

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?