您现在的位置: 新风网管之家 >> 网管培训 >> 网络协议 >> 教程正文

[组图]使用TCP通信心得

作者:未知    教程来源:本站原创    点击数:    更新时间:2009-7-10 【字体:
教程录入:admin    责任编辑:admin 
发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口

>> 相关资讯:

  • 上一篇教程:
  •  
  • 下一篇教程: 没有了
  • 最新五条评论
    您的评论
    姓 名: 评 分: 1分 2分 3分 4分 5分
    ·本站发布内容均为客观表达作者观点,不代表新风网管之家立场,请勿攻击和漫骂
    ·用户发表意见仅代表其个人意见,并且承担一切因发表内容引起的纠纷和责任
    ·本站管理人员有权在不通知用户的情况下删除不符合规定的评论信息或留做证据
    ·请客观的评价您所看到的资讯,提倡就事论事,杜绝漫骂和人身攻击等不文明行为
    前段时间实现了一个小型的C/S架构的多人在线即时通信工具,JIGQQ。其中对使用TCP通信有点心得。
        记得在我大学时代,就用VB做过TCP的通信。当然那时候是很初级的,发送的数据量也很小的应用。当时就觉得,有时候发送的数据接收端不能接收到,有时候呢觉得一次性没有接受完毕,总会丢失一些内容。这和我从书本学到的TCP/IP可靠通信协议的知识完全不匹配,这让我疑惑了很久。直到后来接触的东西越来越多,眼界也逐渐开拓才慢慢意识到问题出在哪。
        应用平台:Windows XP
        开发工具:C++ Builder 6.0

    问题描述
        我在使用Socket接收消息时,将会触发一个接收函数。(BCB中的是ClientRead函数)所以我在此函数处接收信息,并做相应处理。那问题来了:由于传输的数据包都是我自定义的,我明确的知道长度为多多少。可实际效果却是,有时候接不够我期待的长度,而甚至有时候一次接收的数据包长度竟然比我预期的要长10个字节。当时只有设定条件将不满足我预期长度的数据包丢弃。

    问题分析
        看了上面描述,想必大家也明白我的错误在哪了吧?实际是我对Socket的接收机制理解有误。TCP/IP只保证发送包按顺序到达目的地,但可能由于网络状况他会自动分包发送,这样就导致接收端的接受函数每次提交时只有若干数据,不一定是我预期的一个完整的包。可以这样理解,发过来的实际是一个‘流’。
        看来要很好的解决这个问题,那就只有先将接收的数据保存起来,再来做处理。

    处理模型
        为了要保存接收数据,我们首先就要建立一个缓冲区。那第一个问题来了:由于我们要接收的信息是不可预知的,那难道这个缓冲区要无限的扩容?
        可我们的实际PC内存肯定是有限的,所以我们必须建立一套内存缓冲区可以被反复利用的机制 —— 环形队列。
        我们用图来说明环形队列的工作原理:
    /upwglm/allimg/090710/0055303b6-0.JPG
    图1 
    蓝色为写入的数据,绿色为已经读取处理的数据

        看上图1,在正常状态下:Write指针在写入数据,而Read指针在Write指针之前,说明缓冲区后端还有空余空间。
        在指针回滚状态下:Write指针在Read指针之前,说明缓冲区的前端已经有空闲的空间。
        除了这两种状态外,我们不得不再考虑一种即将错误状态:
    /upwglm/allimg/090710/0055305G0-1.JPG
    图2
    蓝色为写入的数据,棕红色为未处理的数据

        看图2,无足够空间:当Write指针回滚,发现无足够空间,将和Read指针发生交集(虚点部分)这显然是不合理的。一部分未处理数据将被覆盖破环。所以我们必须重新调整整个缓冲区。
        重新分配调整:当遇到空间不足,不能实现Write指针回滚的情况,我们只有重新开辟一个更大一点的缓冲区,并把未处理数据(棕红色)和写入数据(蓝色)按顺序复制到新的缓冲区内,并调整好Read和Write指针的位置。最后释放掉原来的缓冲区。
        我们可以看到,经过这样一个过程,我们的缓冲区,将在Read指针处理速度较慢并在处理信息量增大时,逐渐扩容。但是,当扩容到一定程度,将达到一个平衡。因为信息量不可能无限增大,当需处理信息量达到最大值再结合Read指针的不断处理,缓冲区的大小也将稳定下来。
        我们一开头就给此缓冲区命名为‘环形队列’。从以上的图和文字,我们可以形象的理解:由于缓冲区大小最终将稳定,Write和Read指针将无障碍的在缓冲区中不断循环回滚,其运行轨迹,将是一个环形。

    其他知识
        为了要实现上述模型,我们必须要具备一些知识。
        首先,Wirte指针部分,应该在Socket接收函数中去实现。他什么都不干,只管将接收到的数据往环形队列中存放就行了。
        再一个,就是我们的Read指针部分。他需要尽可能快的在环形队列中读取已经储存好的数据,并解析数据后进行相关的操作。最重要一点是,这个过程必须是独立的,在主线程之外运行。
        所以,我们的Write指针部分应该是在主线程,而Read指针部分就必须建立一个额外的线程来进行工作。
        下面我们就看看C++ Builder 6.0下如何实现多线程和需注意的相关事宜。
        1. 深浅相关全局或私有变量
            HANDLE  DealInfHanld;                     //处理各类信息线程句柄
            DWORD   DealInfID;                        //处理各类信息线程ID
            CRITICAL_SECTION    m_csLock;             //用于临界变量互锁

        2. 编写好一个线程处理函数实体
            //处理环形队列中各类信息
            DWORD __stdcall DealInf(LPVOID)