ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Socket模拟Http连接 之 读写操作

Socket模拟Http连接 之 读写操作

原创 Linux操作系统 作者:c00lsun 时间:2009-04-14 10:37:05 0 删除 编辑

上一次介绍了在Symbian平台下如何使用Socket连接指定的网络服务器,这一次,我们继续讲解如何使用Socket进行读写操作。

无论是读操作还是写操作,我们都必须使用之前的SocketConnection类,因为我们要使用该类中的RSocket来进行读写操作,并且,无论是读操作还是写操作,我们都不应该阻碍用户的操作,因此,我们在这里仍旧要使用活动对象来完成功能。

(1)写操作

#ifndef SOCKETWRITER_H
#define SOCKETWRITER_H

// INCLUDES
#include
#include

#include "SocketConnection.h"

// CLASS DECLARATION

/**
*  CSocketWriter
*  该类为CSocketConnection的友元对象,即可以直接方位CSocketConnection的私有属性
*/
class CSocketWriter : public CActive
{
    public:

        ~CSocketWriter( );

        static CSocketWriter* NewL( CSocketConnection* aSocketConn );

        static CSocketWriter* NewLC( CSocketConnection* aSocketConn );
        void Write( const TDesC8& aData );
    protected:
        // from CActive
        virtual void RunL( );
        virtual void DoCancel( );
        virtual TInt RunError( TInt aError );

    private:

        CSocketWriter( );

        void ConstructL( CSocketConnection* aSocketConn );

    private:
        CSocketConnection* m_SocketConn;//Socket连接对象
        TSockXfrLength m_len;//Socket连接中特有的长度对象

};

#endif // SOCKETWRITER_H


#include "SocketWriter.h"
#include "log.h"
#include

CSocketWriter::CSocketWriter( ) :
    CActive( EPriorityStandard )
{
    // No implementation required
}

CSocketWriter::~CSocketWriter( )
{
    Cancel( );
}

CSocketWriter* CSocketWriter::NewLC( CSocketConnection* aSocketConn )
{
    CSocketWriter* self = new ( ELeave ) CSocketWriter( );
    CleanupStack::PushL( self );
    self->ConstructL( aSocketConn );
    return self;
}

CSocketWriter* CSocketWriter::NewL( CSocketConnection* aSocketConn )
{
    CSocketWriter* self = CSocketWriter::NewLC( aSocketConn );
    CleanupStack::Pop( ); // self;
    return self;
}

void CSocketWriter::ConstructL( CSocketConnection* aSocketConn )
{
    m_SocketConn = aSocketConn;
    CActiveScheduler::Add( this );
}

void CSocketWriter::Write( const TDesC8& aData )
{
    if ( !IsActive( ) )
    {
        if ( m_SocketConn )
        {
#if DEBUG
            LOG( _L("be ready to write,context is \n"));
            HBufC* pBuf = HBufC::New( aData.Length( ) );
            TPtr buf = pBuf->Des( );
            CnvUtfConverter::ConvertToUnicodeFromUtf8( buf , aData );
            LOG( _L("%S"),pBuf );
            delete pBuf;
#endif
            m_SocketConn->m_Socket.Send( aData , 0 , iStatus , m_len );
            SetActive( );
        }
        else
        {
            LOG( _L("SocketConn is NULL ,so can not write !"));
        }
    }
    else
    {
#if DEBUG
        LOG( _L("CSocketWriter is busy...") );
#endif
        if ( m_SocketConn && m_SocketConn->m_pSocketObserver )
        {
            m_SocketConn->m_pSocketObserver->OnSocketSent( 0 );
        }
    }
}

void CSocketWriter::RunL( )
{
    if ( iStatus == KErrNone )
    {
        int len = m_len( );
        if ( m_SocketConn && m_SocketConn->m_pSocketObserver )
        {
            m_SocketConn->m_pSocketObserver->OnSocketSent( len );
        }
    }
    else
    {
        if ( m_SocketConn )
        {
            m_SocketConn->DoSocketError( EWrite , iStatus.Int( ) );
        }
    }
}

void CSocketWriter::DoCancel( )
{
    if ( m_SocketConn )
    {
        m_SocketConn->m_Socket.CancelSend( );
    }
}

TInt CSocketWriter::RunError( TInt aError )
{
#if DEBUG
    LOG( _L("SocketWriter error happend ! For reason : %d") , aError );
#endif
    return 0;
}

从代码就可以看出,写操作是相对容易的,只要通过CSocketConnection中的RSocket对象进行发送处理即可,然后到RunL中取得结果,并通过观察者模式返回给观察者结果即可。(如果对观察者模式不太清楚的朋友,而已在网上找些资料,其实挺容易的)


(2) 读操作

#ifndef SOCKETREADER_H
#define SOCKETREADER_H

// INCLUDES
#include
#include

#include "SocketConnection.h"
// CLASS DECLARATION

/**
*  CSocketReader
*  该类为CSocketConnection的友元,即可以直接访问CSocketConnection的私有属性
*/
class CSocketReader : public CActive
{
    public:

        ~CSocketReader( );

        static CSocketReader* NewL( CSocketConnection* aSocketConn );

        static CSocketReader* NewLC( CSocketConnection* aSocketConn );
        void Read();

    protected:
        // from CActive
        virtual void RunL( );
        virtual void DoCancel( );
        virtual TInt RunError( TInt aError );

    private:

        CSocketReader( );

        void ConstructL( CSocketConnection* aSocketConn );

    private:
        CSocketConnection* m_SocketConn;//Socket连接对象
        TBuf8 < 512 > m_readBuffer;//数据缓冲块
        TSockXfrLength m_len;//Socket连接中特有的长度对象

};

#endif // SOCKETREADER_H


#include "SocketReader.h"
#include "log.h"

CSocketReader::CSocketReader( ) :
    CActive( EPriorityStandard )
{
}

CSocketReader::~CSocketReader( )
{
    Cancel();
}

CSocketReader* CSocketReader::NewLC( CSocketConnection* aSocketConn )
{
    CSocketReader* self = new ( ELeave ) CSocketReader( );
    CleanupStack::PushL( self );
    self->ConstructL( aSocketConn );
    return self;
}

CSocketReader* CSocketReader::NewL( CSocketConnection* aSocketConn )
{
    CSocketReader* self = CSocketReader::NewLC( aSocketConn );
    CleanupStack::Pop( ); // self;
    return self;
}

void CSocketReader::ConstructL( CSocketConnection* aSocketConn )
{
    m_SocketConn = aSocketConn;
    CActiveScheduler::Add( this );
}

void CSocketReader::Read( )
{
    if ( m_SocketConn && m_SocketConn->m_ConnectStatus == EConnected )
    {
        if ( !IsActive( ) )
        {
#if DEBUG
            LOG(_L("start to read data from Socket by reader !"));
#endif
            m_SocketConn->m_Socket.RecvOneOrMore(
                    m_readBuffer ,
                    0 ,
                    iStatus ,
                    m_len );
            SetActive( );
        }
#if DEBUG
        else
        {
            LOG(_L("Reader has been Started !"));
        }
#endif
    }
#if DEBUG
    else
    {
        LOG(_L("m_SockdetConn pointer or m_SocketConn->m_ConnectStatus != EConnected"));
    }
#endif

}

void CSocketReader::RunL( )
{
    if ( iStatus == KErrNone && m_SocketConn)
    {
        if(m_SocketConn->m_pSocketObserver)
        {
            m_SocketConn->m_pSocketObserver->OnSocketReceived(m_readBuffer);
            Read( );
        }
#if DEBUG
        else
        {
            LOG(_L("SocketObserver is NULL !"));
        }
#endif
    }
    else
    {
        m_SocketConn->DoSocketError(ERead,iStatus.Int());
    }
}

void CSocketReader::DoCancel( )
{
    if(m_SocketConn )
    {
        m_SocketConn->m_Socket.CancelRead();
    }
}

TInt CSocketReader::RunError( TInt aError )
{
#if DEBUG
    LOG(_L("SocketReader error happend ! Reason : %d"),aError);
#endif
    return 0;
}

读操作中说白了就是在活动对象中不断的调用RSocket对象的RecvOneOrMore()方法,该方法会在有数据时读取数据,没有数据时会等待数据,因此该类也必须是活动对象的,否则,在等待数据的时候就会Block主用户的操作。

由于加入了读写类,因此原先的CSocketConnection也必须做出相应的调整以响应读写操作,这里就把源代码贴给大家

#ifndef SOCKETCONNECTION_H
#define SOCKETCONNECTION_H

// INCLUDES
#include
#include

#include //RSocketServ,RSocket
#include //TInetAddr
//#include "SocketReader.h"
// CLASS DECLARATION

#define DEBUG 1

class MSocketObserver //Socket观察者对象
{
    public:
        virtual void OnSocketConnected( ) = 0; //连接后会被调用,即通知给观察者

        virtual void OnSocketReceived( const TDesC8& data ) = 0; //接收数据时被调用,即通知观察者

        // bytes: count of bytes sent
        virtual void OnSocketSent( TInt bytes ) = 0; //发送数据后会调用,会通知给观察者发送了多少字节

        virtual void OnSocketError( ) = 0; //出错时会调用

};

enum
{
    ENotConnected, ELookingUp, EConnecting, EConnected
};

enum
{
    ERead,EWrite
};

class CSocketConnection : public CActive
{
        friend class CSocketReader;
        friend class CSocketWriter;
        //friend class CSocketWriter;

    public:
        ~CSocketConnection( );

        static CSocketConnection* NewL( MSocketObserver* pSocketObserver );

        static CSocketConnection* NewLC( MSocketObserver* pSocketObserver );

        void ConnectServer( const TDesC& aServerName , TInt aServerPort );
        void DisconnectServer( );
        void DoSocketError(TInt aType,TInt aErr);
        void WriteBySocket(const TDesC8 aData);
    protected:
        // from CActive
        virtual void RunL( );
        virtual void DoCancel( );
        virtual TInt RunError( TInt aError );

    private:
        CSocketConnection( );
        void SocketConnect( TUint32 aAddr );

        void ConstructL( MSocketObserver* pSocketObserver );

    private:
        RSocketServ m_SocketServer;//用于连接Symbian系统服务中的Soket服务
        RSocket m_Socket;//Socket通道
        RHostResolver m_Resolver;//寻找其他设备类,用来通过DNS将主机名解析为ip地址

        HBufC* m_pServerName;//主机名称
        TInt m_ServerPort;
        TNameEntry m_NameEntry;//用来从RHostResolver中返回结果到该对象中

        TInt m_ConnectStatus;

        MSocketObserver* m_pSocketObserver;
        CSocketReader* m_pSocketReader;//Socket读取对象
        CSocketWriter* m_pSocketWriter;//Socket写入对象

};

#include "SocketConnection.h"
#include "SocketReader.h"
#include "SocketWriter.h"
#include
#if DEBUG
#include "log.h"
#endif

CSocketConnection::CSocketConnection( ) :
    CActive( EPriorityStandard )
{
    m_ConnectStatus = 0;
}

CSocketConnection::~CSocketConnection( )
{
    //m_pSocketReader和m_pSocketWriter必须在m_Socket之前释放掉,
    //这是因为这两个对象在释放的时候会调用m_Socket对象

    if ( m_pSocketReader && m_pSocketReader->IsActive( ) )
    {
        m_pSocketReader->Cancel( );
    }
    if ( m_pSocketWriter && m_pSocketWriter->IsActive( ) )
    {
        m_pSocketWriter->Cancel( );
    }
    delete m_pSocketReader , m_pSocketReader = NULL;
    delete m_pSocketWriter , m_pSocketWriter = NULL;

    m_Socket.Close( );
    m_SocketServer.Close( );
    m_Resolver.Close( );
    delete m_pServerName , m_pServerName = NULL;
}

CSocketConnection* CSocketConnection::NewLC( MSocketObserver* pSocketObserver )
{
    CSocketConnection* self = new ( ELeave ) CSocketConnection( );
    CleanupStack::PushL( self );
    self->ConstructL( pSocketObserver );
    return self;
}

CSocketConnection* CSocketConnection::NewL( MSocketObserver* pSocketObserver )
{
    CSocketConnection* self = CSocketConnection::NewLC( pSocketObserver );
    CleanupStack::Pop( ); // self;
    return self;
}

void CSocketConnection::ConstructL( MSocketObserver* pSocketObserver )
{
    m_pSocketReader = CSocketReader::NewL( this );
    m_pSocketWriter = CSocketWriter::NewL( this );

    m_pSocketObserver = pSocketObserver;

    CActiveScheduler::Add( this );
    //连接服务器
    m_SocketServer.Close( );
    m_Socket.Close( );
#if DEBUG
    LOG( _L("be ready to connect RServer(Socket)...") );
#endif
    User::LeaveIfError( m_SocketServer.Connect( ) );
#if DEBUG
    LOG( _L("have connected the RServer(Socket) !") );
    LOG( _L("be ready to open(init) a pointer of RSocket...") );
#endif
    User::LeaveIfError( m_Socket.Open(
            m_SocketServer ,
            KAfInet ,
            KSockStream ,
            KProtocolInetTcp ) );
#if DEBUG
    LOG( _L("have open(init) the pointer of RSocket !") );
#endif
}

/**
* Socket连接函数
* @TUint32 aAddr: Ip地址
*/
void CSocketConnection::SocketConnect( TUint32 aAddr )
{
    m_Socket.Close( );
    User::LeaveIfError( m_Socket.Open(
            m_SocketServer ,
            KAfInet ,
            KSockStream ,
            KProtocolInetTcp ) );
    TInetAddr addr;
    addr.SetPort( m_ServerPort );
    addr.SetAddress( aAddr );
    m_Socket.Connect( addr , iStatus );
    m_ConnectStatus = EConnecting;
#if DEBUG
    LOG( _L("be ready to connecting Socket !") );
#endif
    SetActive( );
}

void CSocketConnection::DisconnectServer( )
{
    if ( m_ConnectStatus == EConnected )
    {
        m_Socket.Close( );
        if ( m_pSocketReader && m_pSocketReader->IsActive( ) )
        {
            m_pSocketReader->Cancel( );
        }
        m_ConnectStatus = ENotConnected;
    }
}

void CSocketConnection::DoSocketError( TInt aType , TInt aErr )
{
    DisconnectServer( );
    m_SocketServer.Close( );
    m_pSocketObserver->OnSocketError( );
    if ( aType == ERead && aErr == -25 )
    {
        return;
    }
#if DEBUG
    LOG( _L("Socket happend error when runtime [reader or writer ] ! %d,%d") ,
    aType , aErr );
#endif
}

void CSocketConnection::WriteBySocket( const TDesC8 aData )
{
    if ( aData.Length( ) > 0 && m_pSocketWriter )
    {
        m_pSocketWriter->Write( aData );
    }
}

/**
* 连接函数
* @TDesC& aServerName: 主机名称
* @TInt aServerName: 主机端口号
*/
void CSocketConnection::ConnectServer(
                                       const TDesC& aServerName ,
                                       TInt aServerPort )
{
    if ( m_ConnectStatus != ENotConnected ) return;

    delete m_pServerName , m_pServerName = NULL;

    if ( aServerName.Length( ) > 0 )
    {
        m_pServerName = aServerName.Alloc( );
    }
    else
    {
        m_pServerName = _L("").Alloc( );
    }
    m_ServerPort = aServerPort;

    if ( m_pServerName->Length( ) <= 0 )
    {
#if DEBUG
        LOG( _L("the ServerName is empty !") );
#endif
        return;
    }
#if DEBUG
    LOG( _L("ServerName is [%S]") , m_pServerName );
    LOG( _L("ServerPort is [%d]") , m_ServerPort );
#endif
    //通过TInetAddr来判断aServerName是否为IP地址
    //如果是ip地址,则直接进入连接过程
    //如果不是ip地址,则进入解析过程,通过RHostResolver类将主机名称解析为ip地址
    TInetAddr addr;
    if ( addr.Input( *m_pServerName ) == KErrNone )
    {
        //进入连接过程
        SocketConnect( addr.Address( ) );
    }
    else
    {
#if DEBUG
        LOG( _L("the ServerName is not a IP address !") );
        LOG(
                _L("be ready to parse ServerName [%S] to IP adress !") ,
                m_pServerName );
#endif
        m_Resolver.Close( );
        m_Resolver.Open( m_SocketServer , KAfInet , KProtocolInetUdp );
        m_Resolver.GetByName( *m_pServerName , m_NameEntry , iStatus );

        m_ConnectStatus = ELookingUp;
        SetActive( );
    }
}

void CSocketConnection::DoCancel( )
{
    m_Socket.Close( );
    m_Resolver.Close( );
}

TInt CSocketConnection::RunError( TInt aError )
{
    return 0;
}

void CSocketConnection::RunL( )
{
    switch ( m_ConnectStatus )
    {
        case EConnecting :
        {
            if ( iStatus == KErrNone )//连接成功
            {
#if DEBUG
                LOG( _L("connecting is sucessful !") );
#endif
                m_ConnectStatus = EConnected;//状态置为已连接状态

                if ( m_pSocketObserver )//如果观察者对象存在
                {
#if DEBUG
                    LOG( _L("call Observer OnSocketConnected() !") );
#endif
                    m_pSocketObserver->OnSocketConnected( );//通知观察者,连接操作完成
                }
                if ( m_pSocketReader )//如果Reader对象不为空(一般都不为空,这里只是进行安全检查)
                {
#if DEBUG
                    LOG( _L("call reader Read() !") );
#endif
                    m_pSocketReader->Read( );//读取数据
                }
            }
            else//连接不成功,状态置为未连接状态
            {
                m_ConnectStatus = ENotConnected;
            }
            break;
        }
        case ELookingUp :
        {
            //搜寻其他设备或连接地址,在Soket应用中,时常需要判断给出的主机地址名是否为一个IP地址,如果不是则需要进行对主机名称解析為IP地址
            m_Resolver.Close( );
            if ( iStatus == KErrNone )//状态无错误,则证明解析地址成功
            {
                TNameRecord nameRecord = m_NameEntry( );
                m_ConnectStatus = ENotConnected;
                //进入连接过程
                SocketConnect( TInetAddr::Cast( nameRecord.iAddr ).Address( ) );
#if DEBUG
                TBuf < 15 > address;
                TInetAddr::Cast( nameRecord.iAddr ).Output( address );
                LOG(
                        _L("Get IP Address [%S]->[%S] sucessfuly !") ,
                        m_pServerName ,
                        &address );
#endif
            }
            else
            {
                m_ConnectStatus = ENotConnected;
#if DEBUG
                LOG(
                        _L("Can not get IP address from ServerName [%S]") ,
                        m_pServerName );
#endif
            }
            break;
        }
    }
}

自此,整个Socket层的连接与读写就已经完成了,我们实际上现在就可以列用它来发送Http请求了,用例如下:

在通知函数中,利用观察者的通知来调用发送函数,即在连接成功后,发送请求,

void CLearningSocketAppUi::OnSocketConnected( )
{
    if ( m_pSocketConn )
    {
        TFileName tfn;
        tfn.Append(
                _L("Get http://mail.163.com/ HTTP/1.1\r\nHost: mail.163.com\r\n\r\n") );
        HBufC8* pBuf = HBufC8::NewL( tfn.Length( ) * 3 );
        TPtr8 buf = pBuf->Des( );
        CnvUtfConverter::ConvertFromUnicodeToUtf8( buf , tfn );
        m_pSocketConn->WriteBySocket( buf );
        delete pBuf;
    }
}

CLearningSocketAppUi本身继承了MSocketObserver类,即CLearningSocketAppUi就是一个观察者对象,这样在OnSocketConnected()方法中就可以发送请求了。

当然,做到现在我们还远远不够,后面我们将封装比较简单的HttpConnection类来帮助我们能够更直接的完成Http请求

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/9059159/viewspace-588945/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2009-03-23

  • 博文量
    31
  • 访问量
    46915