📄 csocket.cpp
字号:
/* External socket implementation using Symbian sockets.
** Copyright (C) 2004-2005 Darrell Karbott (djk2005@users.sf.net)
** This code is free software; you can redistribute it and/or
** modify it under the terms of GNU Lesser General Public License.
** See http://www.gnu.org/ for further details of the LGPL.
*/
#include "CSocket.h"
#include "delmon.h"
#include <e32svr.h>
#include <in_sock.h>
#include <eikenv.h>
////////////////////////////////////////////////////////////
// Debug logging.
// #define ENABLE_CSOCKET_LOGGING 1
#ifdef ENABLE_CSOCKET_LOGGING
void Log(void* p, const char* msg);
void Log(void* p, const char* msg, TInt errCode);
#define CSOCKET_LOG( p, msg ) { Log( p, msg ); }
#define CSOCKET_LOG_ERR( p, msg, errCode ) { Log( p, msg, errCode ); }
#else
#define CSOCKET_LOG( p, msg )
#define CSOCKET_LOG_ERR( p, msg, errCode )
#endif
////////////////////////////////////////////////////////////
// The size of the buffer we use to read the socket.
const TInt RX_BUFFER_LEN = 32768;
// The size of the buffer we keep for writing to
// the socket. If the client manages to write more
// than this much data ahead of the socket, it
// will panic. The client should check BytesWriteable()
// before writing in order to keep this from happening.
// NOTE: I bumped this from 16k.
const TInt TX_BUFFER_LEN = 32768;
// placate linker.
EXPORT_C MSocketObserver::~MSocketObserver() {}
// Helper function to dispatch panics.
static void CSocketPanic(TInt aPanic)
{
User::Panic(KCSocketPanicCategory, aPanic);
}
////////////////////////////////////////////////////////////
// Active Objects to handle name resolution, connecting
// reading and writing.
////////////////////////////////////////////////////////////
class CSocketSetup : public CActive {
public:
CSocketSetup(CSocket* pOwner);
~CSocketSetup();
void ConstructL();
TBool ResolveL(const TDesC& host, TInt port);
TBool ConnectL();
void DoCancel();
void RunL();
TInt RunError();
static CSocketSetup* NewL(CSocket* pOwner);
private:
CSocket* iSocket;
// Members for name resolution.
RHostResolver iResolver;
TNameEntry iHostEntry;
TBufC<128> iAddressName;
TInetAddr iAddr;
TInt iPort;
};
class CSocketTx : public CActive {
public:
CSocketTx(CSocket* pOwner);
~CSocketTx();
void ConstructL();
void EnqueueWrite();
void Write(const TDesC8&);
TInt SendLen() const;
TInt BytesBuffered() const;
void DoCancel();
// calls CSocket::Notify
void RunL();
TInt RunError();
static CSocketTx* NewL(CSocket* pOwner);
private:
CSocket* iSocket;
TInt iBytesSent;
HBufC8* iBuffer;
TPtrC8 iCurrentWrite;
};
class CSocketRx : public CActive {
public:
CSocketRx(CSocket* pOwner, TBool autoRestart);
~CSocketRx();
void ConstructL();
TBool Read(TBool bEnabled);
const TDesC8& RecvdData() const;
void DoCancel();
// calls CSocket::Notify
void RunL();
TInt RunError();
static CSocketRx* NewL(CSocket* pOwner, TBool autoRestart = ETrue);
private:
void EnqueueRead();
TSockXfrLength iXfrLen;
TBuf8<RX_BUFFER_LEN> iBuffer;
CSocket* iSocket;
TBool iAutoRestart;
TBool iSendLastChar;
TUint8 iLastChar;
};
////////////////////////////////////////////////////////////
CSocketSetup* CSocketSetup::NewL(CSocket* pOwner)
{
CSocketSetup* self = new (ELeave) CSocketSetup(pOwner);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(); // self
return self;
}
CSocketSetup::CSocketSetup(CSocket* pOwner)
: CActive(EPriorityStandard), iSocket(pOwner)
{
}
CSocketSetup::~CSocketSetup()
{
Cancel();
iResolver.Close();
}
void CSocketSetup::ConstructL()
{
CActiveScheduler::Add(this);
}
// Starts name resolution which starts connection
// when it finishes.
TBool CSocketSetup::ResolveL(const TDesC& host, TInt port)
{
CSOCKET_LOG(this, "CSocketSetup::ResolveL -- called");
__ASSERT_ALWAYS(!IsActive(), CSocketPanic(CSocket::INVFAILED));
__ASSERT_ALWAYS(iSocket->State() == CSocket::EClosed,
CSocketPanic(CSocket::INVFAILED));
iAddressName = host;
iPort = port;
iSocket->SetState(CSocket::EResolving);
if(iSocket->Notify(MSocketObserver::EResolving)) { return ETrue; }
if (iSocket->State() != CSocket::EResolving) { return EFalse; }
TInt result = iResolver.Open(*(iSocket->SocketServ()),
KAfInet,
KProtocolInetTcp);
if (result != KErrNone) {
iSocket->HandleError(result);
}
User::LeaveIfError(result);
iResolver.GetByName(iAddressName, iHostEntry, iStatus);
SetActive();
return EFalse;
}
// hmmm... doesn't leave.
TBool CSocketSetup::ConnectL()
{
CSOCKET_LOG(this, "CSocketSetup::ConnectL -- called");
iSocket->SetState(CSocket::EConnecting);
if (iSocket->Notify(MSocketObserver::EConnecting)) { return ETrue; };
if (iSocket->State() != CSocket::EConnecting) { return EFalse; }
// Force error on timeout.
// djk 20040212 this didn't make any difference
//iSocket->Socket()->SetOpt(KSolInetTcp,KSoTcpKeepAlive, 1);
// Note: no return code.
iSocket->Socket()->Connect(iAddr, iStatus);
SetActive();
return EFalse;
}
void CSocketSetup::DoCancel()
{
if (iSocket->State() == CSocket::EResolving) {
iResolver.Cancel();
iResolver.Close();
}
else if (iSocket->State() == CSocket::EConnecting) {
iSocket->Socket()->CancelConnect();
}
}
void CSocketSetup::RunL()
{
if (iStatus == KErrNone) {
switch (iSocket->State()) {
case CSocket::EResolving:
// Stash address from name resolver.
iAddr = iHostEntry().iAddr;
// Resolver just gives us the name, we need to set
// the port explicitly.
iAddr.SetPort(iPort);
iResolver.Close();
if (ConnectL()) {
return;
}
break;
case CSocket::EConnecting:
CSOCKET_LOG(this, "CSocketSetup::RunL -- Connected");
iSocket->SetState(CSocket::EConnected);
if (iSocket->Notify(MSocketObserver::EConnected)) { return;}
if (iSocket->State() != CSocket::EConnected) { return; }
break;
case CSocket::EConnected:
case CSocket::EClosed:
case CSocket::EError:
break;
}
}
else {
CSOCKET_LOG_ERR(this, "CSocketSetup::RunL -- iStatus != KErrNone, err: ", iStatus.Int());
iSocket->HandleError(iStatus.Int());
}
}
TInt CSocketSetup::RunError()
{
CSOCKET_LOG(this, "CSocketSetup::RunError -- called");
iSocket->HandleError(KErrCouldNotConnect);
return KErrNone;
}
CSocketTx* CSocketTx::NewL(CSocket* pOwner)
{
CSocketTx* self = new (ELeave) CSocketTx(pOwner);
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop(); // self
return self;
}
CSocketTx::CSocketTx(CSocket* pOwner)
: CActive(EPriorityStandard), iSocket(pOwner), iBytesSent(0)
{
}
CSocketTx::~CSocketTx()
{
Cancel();
delete iBuffer;
iBuffer = NULL;
}
void CSocketTx::ConstructL()
{
CActiveScheduler::Add(this);
// hmmmm...Make this a parameter?
// + 1 so we have space to null terminate.
iBuffer = HBufC8::NewMaxL(TX_BUFFER_LEN + 1);
// Set the default size to 0.
// This is not done autmatically.
iBuffer->Des().Zero();
}
// It is legal to call this when the buffer is empty.
void CSocketTx::EnqueueWrite()
{
__ASSERT_ALWAYS(!IsActive(),CSocketPanic(CSocket::INVFAILED));
if (iCurrentWrite.Size() > 0) {
// Release previous data.
TPtr8 ptr(iBuffer->Des());
ptr.Delete(0, iCurrentWrite.Size());
__ASSERT_DEBUG( ptr.Size() == iBuffer->Des().Size(),
CSocketPanic(CSocket::INVFAILED));
}
// I used to do this, but I would see rare silent write failures.
//iCurrentWrite.Set(iBuffer->Des());
// Bound size of writes. Why 1024? magick number. HACK
TInt nBytes = iBuffer->Size() > 1024 ? 1024 : iBuffer->Size();
iCurrentWrite.Set(iBuffer->Des().Ptr(), nBytes);
if (iCurrentWrite.Size() > 0) {
iSocket->Socket()->Write(iCurrentWrite, iStatus);
SetActive();
}
}
void CSocketTx::Write(const TDesC8& data)
{
__ASSERT_ALWAYS(iSocket->State() == CSocket::EConnected,
CSocketPanic(CSocket::INVFAILED));
// Hmmm.. can't just resize the buffer when we run out of space because
// it might be in use.
__ASSERT_ALWAYS(data.Length() + iBuffer->Des().Size() <= iBuffer->Des().MaxLength() - 1,
CSocketPanic(CSocket::INVFAILED));
// Make a deep copy of the buffer.
TPtr8 ptr(iBuffer->Des());
ptr.Append(data);
if (!IsActive()) {
EnqueueWrite();
}
// Otherwise, RunL() will write buffered data when
// the current write finishes.
}
TInt CSocketTx::SendLen() const
{
return iBytesSent;
}
TInt CSocketTx::BytesBuffered() const
{
return iBuffer->Length();
}
void CSocketTx::DoCancel()
{
iSocket->Socket()->CancelWrite();
}
void CSocketTx::RunL()
{
if (iStatus == KErrNone) {
// Set length before callback.
iBytesSent = iCurrentWrite.Size();
if (iSocket->State() == CSocket::EConnected) {
if (iSocket->Notify(MSocketObserver::EWrite)) { return; }
if (iSocket->State() != CSocket::EConnected) { return; }
// Client code in the callback called CSocket::Write().
if (IsActive()) { return; }
// Kick off the next write if there's any buffered
// data left.
EnqueueWrite();
}
else {
// Hmmm... assert here?
CSOCKET_LOG_ERR(this, "CSocketTx::RunL -- write in unexpected state!, state", iSocket->State());
}
}
else {
CSOCKET_LOG_ERR(this, "CSocketTx::RunL -- iStatus != KErrNone, err: ", iStatus.Int());
iSocket->HandleError(iStatus.Int());
}
}
TInt CSocketTx::RunError()
{
CSOCKET_LOG(this, "CSocketTx::RunError -- called");
iSocket->HandleError(KErrUnknown);
return KErrNone;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -