📄 emsocket.cpp
字号:
//DWORD tempStartSendTick = ::GetTickCount();
lastSent = ::GetTickCount();
uint32 result = CAsyncSocketEx::Send(sendbuffer+sent,tosend); // deadlake PROXYSUPPORT - changed to AsyncSocketEx
if (result == (uint32)SOCKET_ERROR){
uint32 error = GetLastError();
if (error == WSAEWOULDBLOCK){
m_bBusy = true;
//m_wasBlocked = true;
sendLocker.Unlock();
SocketSentBytes returnVal = { true, sentStandardPacketBytesThisCall, sentControlPacketBytesThisCall };
return returnVal; // Send() blocked, onsend will be called when ready to send again
} else{
// Send() gave an error
anErrorHasOccured = true;
//DEBUG_ONLY( AddDebugLogLine(true,"EMSocket: An error has occured: %i", error) );
}
} else {
// we managed to send some bytes. Perform bookkeeping.
m_bBusy = false;
m_hasSent = true;
sent += result;
// Log send bytes in correct class
if(m_currentPacket_is_controlpacket == false) {
sentStandardPacketBytesThisCall += result;
if(m_currentPackageIsFromPartFile == true) {
m_numberOfSentBytesPartFile += result;
} else {
m_numberOfSentBytesCompleteFile += result;
}
} else {
sentControlPacketBytesThisCall += result;
m_numberOfSentBytesControlPacket += result;
}
}
}
if (sent == sendblen){
// we are done sending the current package. Delete it and set
// sendbuffer to NULL so a new packet can be fetched.
delete[] sendbuffer;
sendbuffer = NULL;
sendblen = 0;
if(!m_currentPacket_is_controlpacket) {
m_actualPayloadSizeSent += m_actualPayloadSize;
m_actualPayloadSize = 0;
lastFinishedStandard = ::GetTickCount(); // reset timeout
m_bAccelerateUpload = false; // Safe until told otherwise
}
sent = 0;
}
}
if(onlyAllowedToSendControlPacket && (!controlpacket_queue.IsEmpty() || sendbuffer != NULL && m_currentPacket_is_controlpacket)) {
// enter control packet send queue
// we might enter control packet queue several times for the same package,
// but that costs very little overhead. Less overhead than trying to make sure
// that we only enter the queue once.
theApp.uploadBandwidthThrottler->QueueForSendingControlPacket(this, HasSent());
}
//CleanSendLatencyList();
sendLocker.Unlock();
SocketSentBytes returnVal = { !anErrorHasOccured, sentStandardPacketBytesThisCall, sentControlPacketBytesThisCall };
return returnVal;
}
uint32 CEMSocket::GetNextFragSize(uint32 current, uint32 minFragSize) {
if(current % minFragSize == 0) {
return current;
} else {
return minFragSize*(current/minFragSize+1);
}
}
/**
* Decides the (minimum) amount the socket needs to send to prevent timeout.
*
* @author SlugFiller
*/
uint32 CEMSocket::GetNeededBytes() {
sendLocker.Lock();
if (byConnected == ES_DISCONNECTED) {
sendLocker.Unlock();
return 0;
}
if (!((sendbuffer && !m_currentPacket_is_controlpacket) || !standartpacket_queue.IsEmpty())) {
// No standard packet to send. Even if data needs to be sent to prevent timout, there's nothing to send.
sendLocker.Unlock();
return 0;
}
if (((sendbuffer && !m_currentPacket_is_controlpacket)) && !controlpacket_queue.IsEmpty())
m_bAccelerateUpload = true; // We might be trying to send a block request, accelerate packet
uint32 sendgap = ::GetTickCount() - lastCalledSend;
uint64 timetotal = m_bAccelerateUpload?45000:90000;
uint64 timeleft = ::GetTickCount() - lastFinishedStandard;
uint64 sizeleft, sizetotal;
if (sendbuffer && !m_currentPacket_is_controlpacket) {
sizeleft = sendblen-sent;
sizetotal = sendblen;
}
else {
sizeleft = sizetotal = standartpacket_queue.GetHead().packet->GetRealPacketSize();
}
sendLocker.Unlock();
if (timeleft >= timetotal)
return sizeleft;
timeleft = timetotal-timeleft;
if (timeleft*sizetotal >= timetotal*sizeleft) {
// don't use 'GetTimeOut' here in case the timeout value is high,
if (sendgap > SEC2MS(20))
return 1; // Don't let the socket itself time out - Might happen when switching from spread(non-focus) slot to trickle slot
return 0;
}
uint64 decval = timeleft*sizetotal/timetotal;
if (!decval)
return sizeleft;
if (decval < sizeleft)
return sizeleft-decval+1; // Round up
else
return 1;
}
// pach2:
// written this overriden Receive to handle transparently FIN notifications coming from calls to recv()
// This was maybe(??) the cause of a lot of socket error, notably after a brutal close from peer
// also added trace so that we can debug after the fact ...
int CEMSocket::Receive(void* lpBuf, int nBufLen, int nFlags)
{
// EMTrace("CEMSocket::Receive on %d, maxSize=%d",(SOCKET)this,nBufLen);
int recvRetCode = CAsyncSocketEx::Receive(lpBuf,nBufLen,nFlags); // deadlake PROXYSUPPORT - changed to AsyncSocketEx
switch (recvRetCode) {
case 0:
//EMTrace("CEMSocket::##Received FIN on %d, maxSize=%d",(SOCKET)this,nBufLen);
// FIN received on socket // Connection is being closed by peer
//ASSERT (false);
if ( 0 == AsyncSelect(FD_CLOSE|FD_WRITE) ) { // no more READ notifications ...
//int waserr = GetLastError(); // oups, AsyncSelect failed !!!
ASSERT(false);
}
return 0;
case SOCKET_ERROR:
switch(GetLastError()) {
case WSANOTINITIALISED:
ASSERT(false);
EMTrace("CEMSocket::OnReceive:A successful AfxSocketInit must occur before using this API.");
break;
case WSAENETDOWN:
ASSERT(true);
EMTrace("CEMSocket::OnReceive:The socket %d received a net down error",(SOCKET)this);
break;
case WSAENOTCONN: // The socket is not connected.
EMTrace("CEMSocket::OnReceive:The socket %d is not connected",(SOCKET)this);
break;
case WSAEINPROGRESS: // A blocking Windows Sockets operation is in progress.
EMTrace("CEMSocket::OnReceive:The socket %d is blocked",(SOCKET)this);
break;
case WSAEWOULDBLOCK: // The socket is marked as nonblocking and the Receive operation would block.
EMTrace("CEMSocket::OnReceive:The socket %d would block",(SOCKET)this);
break;
case WSAENOTSOCK: // The descriptor is not a socket.
EMTrace("CEMSocket::OnReceive:The descriptor %d is not a socket (may have been closed or never created)",(SOCKET)this);
break;
case WSAEOPNOTSUPP: // MSG_OOB was specified, but the socket is not of type SOCK_STREAM.
break;
case WSAESHUTDOWN: // The socket has been shut down; it is not possible to call Receive on a socket after ShutDown has been invoked with nHow set to 0 or 2.
EMTrace("CEMSocket::OnReceive:The socket %d has been shut down",(SOCKET)this);
break;
case WSAEMSGSIZE: // The datagram was too large to fit into the specified buffer and was truncated.
EMTrace("CEMSocket::OnReceive:The datagram was too large to fit and was truncated (socket %d)",(SOCKET)this);
break;
case WSAEINVAL: // The socket has not been bound with Bind.
EMTrace("CEMSocket::OnReceive:The socket %d has not been bound",(SOCKET)this);
break;
case WSAECONNABORTED: // The virtual circuit was aborted due to timeout or other failure.
EMTrace("CEMSocket::OnReceive:The socket %d has not been bound",(SOCKET)this);
break;
case WSAECONNRESET: // The virtual circuit was reset by the remote side.
EMTrace("CEMSocket::OnReceive:The socket %d has not been bound",(SOCKET)this);
break;
default:
EMTrace("CEMSocket::OnReceive:Unexpected socket error %x on socket %d",GetLastError(),(SOCKET)this);
break;
}
break;
default:
// EMTrace("CEMSocket::OnReceive on %d, receivedSize=%d",(SOCKET)this,recvRetCode);
return recvRetCode;
}
return SOCKET_ERROR;
}
// deadlake PROXYSUPPORT ( RESETS LAYER CHAIN BY MAVERICK )
void CEMSocket::RemoveAllLayers()
{
CAsyncSocketEx::RemoveAllLayers();
// ProxyLayer Destruction
if (m_pProxyLayer)
{
delete m_pProxyLayer;
m_pProxyLayer = NULL;
}
}
int CEMSocket::OnLayerCallback(const CAsyncSocketExLayer *pLayer, int nType, int nParam1, int nParam2)
{
ASSERT(pLayer);
if (nType==LAYERCALLBACK_STATECHANGE)
{
CString logline;
if (pLayer==m_pProxyLayer)
{
//logline.Format(_T("ProxyLayer changed state from %d to %d"), nParam2, nParam1);
//AddLogLine(false,logline);
}else
//logline.Format(_T("Layer @ %d changed state from %d to %d"), pLayer, nParam2, nParam1);
//AddLogLine(false,logline);
return 1;
}
else if (nType==LAYERCALLBACK_LAYERSPECIFIC)
{
if (pLayer==m_pProxyLayer)
{
switch (nParam1)
{
// changed by deadlake -> errormessages could be ignored -> there's not a problem with the connection -
// only the proxyserver handles the connections to low ( small bandwidth? )
case PROXYERROR_NOCONN:{
//TODO: This error message(s) should be outputed only during startup - otherwise we'll see a lot of
//them in the log window which would be of no use.
if (thePrefs.GetShowProxyErrors()){
CString strError(_T("Can't connect to proxy"));
CString strErrInf;
if (nParam2 && GetErrorMessage(nParam2, strErrInf))
strError += _T(" - ") + strErrInf;
AddLogLine(false, _T("%s"), strError);
}
break;
}
case PROXYERROR_REQUESTFAILED:{
//TODO: This error message(s) should be outputed only during startup - otherwise we'll see a lot of
//them in the log window which would be of no use.
if (thePrefs.GetShowProxyErrors()){
CString strError(_T("Proxy request failed"));
if (nParam2){
strError += _T(" - ");
strError += (LPCSTR)nParam2;
}
AddLogLine(false, _T("%s"), strError);
}
break;
}
case PROXYERROR_AUTHTYPEUNKNOWN:
AddLogLine(false,_T("Required authtype reported by proxy server is unknown or unsupported"));
break;
case PROXYERROR_AUTHFAILED:
AddLogLine(false,_T("Authentification failed"));
break;
case PROXYERROR_AUTHNOLOGON:
AddLogLine(false,_T("Proxy requires authentification"));
break;
case PROXYERROR_CANTRESOLVEHOST:
AddLogLine(false,_T("Can't resolve host of proxy"));
break;
default:{
AddLogLine(false,_T("Proxy error - %s"), GetProxyError(nParam1));
}
}
}
}
return 1;
}
// end deadlake
/**
* Removes all packets from the standard queue that don't have to be sent for the socket to be able to send a control packet.
*
* Before a socket can send a new packet, the current packet has to be finished. If the current packet is part of
* a split packet, then all parts of that split packet must be sent before the socket can send a control packet.
*
* This method keeps in standard queue only those packets that must be sent (rest of split packet), and removes everything
* after it. The method doesn't touch the control packet queue.
*/
void CEMSocket::TruncateQueues() {
sendLocker.Lock();
// Clear the standard queue totally
// Please note! There may still be a standardpacket in the sendbuffer variable!
for(POSITION pos = standartpacket_queue.GetHeadPosition(); pos != NULL; )
delete standartpacket_queue.GetNext(pos).packet;
standartpacket_queue.RemoveAll();
sendLocker.Unlock();
}
#ifdef _DEBUG
void CEMSocket::AssertValid() const
{
CAsyncSocketEx::AssertValid();
const_cast<CEMSocket*>(this)->sendLocker.Lock();
ASSERT( byConnected==ES_DISCONNECTED || byConnected==ES_NOTCONNECTED || byConnected==ES_CONNECTED );
CHECK_BOOL(m_ProxyConnectFailed);
CHECK_PTR(m_pProxyLayer);
(void)downloadLimit;
CHECK_BOOL(downloadLimitEnable);
CHECK_BOOL(pendingOnReceive);
//char pendingHeader[PACKET_HEADER_SIZE];
pendingHeaderSize;
CHECK_PTR(pendingPacket);
(void)pendingPacketSize;
CHECK_ARR(sendbuffer, sendblen);
(void)sent;
controlpacket_queue.AssertValid();
standartpacket_queue.AssertValid();
CHECK_BOOL(m_currentPacket_is_controlpacket);
//(void)sendLocker;
(void)m_numberOfSentBytesCompleteFile;
(void)m_numberOfSentBytesPartFile;
(void)m_numberOfSentBytesControlPacket;
CHECK_BOOL(m_currentPackageIsFromPartFile);
(void)lastCalledSend;
(void)m_actualPayloadSize;
(void)m_actualPayloadSizeSent;
const_cast<CEMSocket*>(this)->sendLocker.Unlock();
}
#endif
#ifdef _DEBUG
void CEMSocket::Dump(CDumpContext& dc) const
{
CAsyncSocketEx::Dump(dc);
}
#endif
void CEMSocket::DataReceived(const BYTE*, UINT)
{
ASSERT(0);
}
UINT CEMSocket::GetTimeOut() const
{
return m_uTimeOut;
}
void CEMSocket::SetTimeOut(UINT uTimeOut)
{
m_uTimeOut = uTimeOut;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -