完成端口学习之简易聊天室

本文介绍了作者学习C++后,使用SOCKET完成端口实现的无界面C/S聊天工具。代码参考了小猪前辈的文章,并通过AcceptEx获取客户端信息。虽然代码冗余且有待优化,但作为学习过程的记录,作者计划在未来改进。
摘要由CSDN通过智能技术生成

严格的来说吧,这个算是我学习C++后第一个写的不成熟的小程序,现在还没有毕业,一切的东西都算是学习中的小小动手玩乐。
这个是基于SOCKET完成端口而实现的C/S简易聊天工具,没有前端界面,就代码而言应该来说是很冗余和漏洞百出的。贴出来的目的在于将来有所成长后,回头看看曾经写的所谓“学生式的代码”。

这份代码也是具体学了小猪前辈的代码而有所得的东西。
原文:http://blog.csdn.net/piggyxp/article/details/6922277
具体的介绍就不多说了,毕竟这是写给以后的自己看的。
大体思路为:
1、构建IO操作结构体以及对应的数组
2、构建Socket操作的结构体以及对应的数组
3、用AcceptEx指针来获取相应的客户端信息


1.对于结构体的定义

 在IO操作结构体当中,定义重叠结构体,然后再后面接上每个网络操作对应的Socket以及缓冲区等信息。
typedef struct _PER_IO_CONTEXT
{
    OVERLAPPED          m_Overlapped;               //每个socket有一个重叠结构
    SOCKET              m_SocketAccept;             //这个网络操作对应的socket
    WSABUF              m_wsaBuf;                   //缓冲区
    char                m_szBuffer[MAX_BUFFER_LEN]; //WSABUF存放字符的缓冲区
    OPERATION_TPYE      m_OpType;                   //枚举对象,标识网络操作的类型
    //初始化资源
    _PER_IO_CONTEXT()
    {
        ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
        ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
        m_SocketAccept = INVALID_SOCKET;
        m_wsaBuf.buf = m_szBuffer;
        m_wsaBuf.len = MAX_BUFFER_LEN;
        m_OpType = NONE;
    }
    //释放socket资源
    ~_PER_IO_CONTEXT()
    {
        if (m_SocketAccept != INVALID_SOCKET)
        {
            ZeroMemory(&m_Overlapped, sizeof(m_Overlapped));
            ZeroMemory(&m_szBuffer, MAX_BUFFER_LEN);
            m_wsaBuf.buf = m_szBuffer;
            m_wsaBuf.len = MAX_BUFFER_LEN;
            m_OpType = NONE;
        }
    }
    // 重置缓冲区内容
    void ResetBuffer()
    {
        ZeroMemory(m_szBuffer, MAX_BUFFER_LEN);
    }
}PER_IO_CONTEXT;
然后就是Socket对应的结构体了
typedef struct _PER_SOCKET_CONTEXT
{
    SOCKET          m_Socket;               //每个客户端连接的socket
    SOCKADDR_IN     m_ClientAddr;           //每个客户端的地址
    char            m_username[40];         //存放用户名
    //初始化
    _PER_SOCKET_CONTEXT() {
        m_Socket = INVALID_SOCKET;
        //将m_ClientAddr这个位置后面的m_ClientAddr长度个字节用0填上
        //即对m_ClientAddr进行清零
        memset(&m_ClientAddr, 0, sizeof(m_ClientAddr));
    }
    //释放资源
    ~_PER_SOCKET_CONTEXT() 
    {
        if (m_Socket != INVALID_SOCKET)
        {
            closesocket(m_Socket);
            m_Socket = INVALID_SOCKET;
        }
    }

}PER_SOCKET_CONTEXT;
当然,每个结构体也有对应的操作,因为对于代码编程理解有限,所以采用比较笨拙的方式定义相应的方法
class PER_IO_CONTEXT_ARR
{
   
private:    
    //创建io结构体数组,用来存放每个socket对应的io操作
    PER_IO_CONTEXT *IO_CONTEXT_ARR[2048];
public:
    int num=0;  //计数
    //获取编号
    PER_IO_CONTEXT * GetARR(int i)
    {
        return IO_CONTEXT_ARR[i];
    }
    //循环遍历IO操作数组,通过遍历所有位置,如果为0.则表示可以存放新的IO操作,后面只需要将数组全部填0,就可以初始化数组,并且在移除IO操作后,可以在原来位置上放心的IO操作。
    PER_IO_CONTEXT* GetNewIoContext()
    {
        for (int i = 0; i < 2048; i++)
        {
            //如果某一个IO_CONTEXT_ARRAY[i]为0,表示哪一个位可以放入PER_IO_CONTEXT  
            if (IO_CONTEXT_ARR[i] == 0)
            {
                IO_CONTEXT_ARR[i] = new PER_IO_CONTEXT();
                num++;
                return IO_CONTEXT_ARR[i];
            }
        }
    }
    //如果IO操作数组中某个io操作完成,那么移除该IO操作,并且用0替换该位置的值,以方便新的IO操作存放
    //新增了一个IO操作,NUM++ 所以只需要循环遍历前num个成员就可以了
    void RemoveContext(PER_IO_CONTEXT * RContext)
    {
        for (int i = 0; i < num; i++)
        {
            if (IO_CONTEXT_ARR[i] == RContext)
            {
                IO_CONTEXT_ARR[i]->~_PER_IO_CONTEXT();
                IO_CONTEXT_ARR[i] = 0;
                //移除一个IO操作,计数-1
                num--;
                break;
            }
        }
        return;
    }

};
class PER_SOCKET_CONTEXT_ARR
{
private:
    //创建socket结构体数组
    PER_SOCKET_CONTEXT * SOCKET_CONTEXT_ARR[2048];
public:
    int num = 0; //计数
    //循环判定socket结构体数组中那个位置为0,表示该位置可以存放新的socket信息,同IO操作数组一样,初始化后,就可以从0位置开始存放,并且因为和操作是一对一关系,因此socket结构体数组和io操作数组下标应该是对应关系,这样在后续相关操作过程中,通过下标查询相应的数组,就可以得到例如客户端的地址,端口,和相对应的socket。
    PER_SOCKET_CONTEXT* GetNewSocketContext(SOCKADDR_IN* addr, char *u)
    {
        for (int i = 0; i < 2048; i++)
        {
            //如果某个位置上的值为0,表示该位置可以存放新的socket结构体信息
            if (SOCKET_CONTEXT_ARR[i] == 0) 
            {
                SOCKET_CONTEXT_ARR[num] = new PER_SOCKET_CONTEXT();
                //该位置存放新的socket结构体信息,同时我们也把客户端的相应信息写入进去
                //将传入进来的addr拷贝进SOCKET_CONTEXT_ARR指针指向的位置,长度为地址长度
                memcpy(&(SOCKET_CONTEXT_ARR[num]->m_ClientAddr),addr,sizeof(SOCKADDR_IN));
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值