您的位置:程序门 -> delphi -> 网络通信/分布式开发



tclientwinsocket客户端接收数据问题。


[收藏此页] [打印本页]选择字色:背景色:字体:[][][]


tclientwinsocket客户端接收数据问题。[已结贴,结贴人:piaoxueskysz]
发表于:2008-01-17 16:19:06 楼主
我写的一个客户端收发主线程。刚开始线程可以正常收发,
但在有的电脑上运行一段时间后只能发不能收。
服务端是使用完成端口完成的,也可以正常给客户端发送数据。
以下是我的主线程,代码,请高手指点指点。
拜。。。。。。。。。。。。。。。。


unit   u_mainthread;

interface
uses   windows,   messages,   sysutils,   variants,   classes,   graphics,   controls,   forms,
          dialogs,   stdctrls,     syncobjs,   extctrls,shareunit,iocpcomp,   scktcomp,winsock2;

type
    trecmsgevent   =   procedure(arecstr:   string)   of   object;
    tclientthread=class(tthread)
    private
        fhost:   string;
        faddmsg:   trecmsgevent;
        fsocket:   tclientwinsocket;
        lastrecvtest:   tdatetime;                       //最后收到测试字符串的时间
    protected
        procedure   EXECute;   override;
    public
        constructor   create(ahost:   string;   aaddmsg:   trecmsgevent);
        destructor   destroy;   override;
    end;
{   tclient   }
implementation
uses   u_wy108clientmain;
constructor   tclientthread.create;
begin
    freeonterminate   :=   true;
    inherited   create(false);
    lastrecvtest   :=   now();
    fhost   :=   ahost;
    faddmsg   :=   aaddmsg;
    fsocket   :=   tclientwinsocket.create(integer(not(0)));
    //fsocket.clienttype   :=   ctnonblocking;
    //采用阻塞模式
    fsocket.clienttype   :=   ctblocking;
end;

destructor   tclientthread.destroy;
begin
    fsocket.free;
    inherited   destroy;
end;

procedure   tclientthread.EXECute;
const
    sizeint   =   sizeof(integer);
    sizeblock   =   sizeof(tdatablock);
    data:   tdatablock   =   (len:   14;   content:   'testpiaoxuesky');
    //检查网络异常过程
    function   isclose(socket,   event:   cardinal):   boolean;
    var
        network:   twsanetworkevents;
    begin
        result   :=   true;
        fillchar(network,   sizeof(network),   0);
        if   wsaenumnetworkevents(fsocket.sockethandle,   event,   @network)   =   -1   then   exit;
        {   close   消息   }
        result   :=   ((network.lnetworkevents   and   fd_close)   =   fd_close)   and
              (network.ierrorcode[fd_close_bit]   <>   0);
    end;
var
    msg:   tmsg;             //线程消息
    d:   tdatablock;     //接收数组
    timeout,   retlen:   integer;   //超时设置   接收数组大小
    event:   thandle;                       //事件句柄
    conn:   tdatablock   ;                 //发送数组
    hadsendconn:   boolean;           //是否已经登陆
    asendmsg:   string;                   //发送字串
    count:   integer;                       //发送数组大小
begin
    try
        fsocket.open(fhost,   fhost,   '',   8309);   //连接服务端
        timeout   :=   2000;                                             //2秒超时
        hadsendconn   :=   false;
        setsockopt(fsocket.sockethandle,   sol_socket,   so_rcvtimeo,   @timeout,   sizeof(timeout));
    except
        exit;
    end;
    peekmessage(msg,   0,   0,   0,   pm_noremove);
    event   :=   wsacreateevent;     //创建事件句柄
    try
        wsaeventselect(fsocket.sockethandle,   event,   fd_read   or   fd_close);
        while   (not   terminated)   and   (fsocket.connected=true)   do
            case   msgwaitformultipleobjects(1,   event,   false,   500,   qs_allinput)   of
                //接收网络消息部分
                wait_object_0:
                begin
                    if   isclose(fsocket.sockethandle,   event)   then
                    begin
                        {   'server   close'   ;   }
                        break;
                    end;
                    //初始化接收数组
                    fillchar(d,   sizeblock,   0);
                    retlen   :=   fsocket.receivebuf(d.len,   sizeint);
                    if   retlen   =   0   then
                        break;
                    if   retlen   <>   sizeint   then
                    begin
                        continue;
                    end;
                    retlen   :=   fsocket.receivebuf(d.content,   d.len);
                    if   retlen   <>   d.len   then
                    begin
                        continue;
                    end;
                    //不合规格接收字串丢失
                    if       (copy(d.content,1,4) <> 'succ')   and     (copy(d.content,1,4) <> 'news')
                    and     (copy(d.content,1,4) <> 'hqsx')   and     (copy(d.content,1,4) <> 'gpsx')
                    and     (copy(d.content,1,4) <> 'mmsx')   and     (copy(d.content,1,4) <> 'gdgg')
                    and     (copy(d.content,1,4) <> 'succ')   and     (copy(d.content,1,4) <> 'lscc')
                    and     (copy(d.content,1,4) <> 'ldis')   and     (copy(d.content,1,4) <> 'lhal')
                    and     (copy(d.content,1,4) <> 'oout')   and     (copy(d.content,1,4) <> 'trys')
                    and     (copy(d.content,1,4) <> 'upda')   and     (copy(d.content,1,4) <> 'test')
                    and     (copy(d.content,1,4) <> 'hell')
                    then
                    begin
                      continue;
                    end;
                    //测试字串
                    if   (copy(d.content,1,4)='test')   or   (copy(d.content,1,4)='hell')   then
                    begin
                        //记录最后一个接收到的测试字串事件
                        lastrecvtest   :=   now();
                        //writeerrorlog('last   rec   test   message-> '+d.content);
                        continue;
                    end;
                    entercriticalsection(critical1);//进入临界段
                    //给一个全局变量赋值,因为在主界面接收到wm_getmes消息后需要读取clientrecvmsg内容。
                    //所以我在这里做了同步操作。
                    clientrecvmsg   :=   '';
                    clientrecvmsg   :=   d.content;
                    leavecriticalsection(critical1);//退出临界段
                    //发送消息刷新界面元素
                    postmessage(frm_wyclientmain.handle,   wm_getmes,   0,   0);
                    //重置事件句柄
                    wsaresetevent(event);
                end;
                wait_object_0   +   1:
                begin
                    //发送网络消息部分
                    if   peekmessage(msg,   0,   0,   0,   pm_remove)   then
                        begin
                            case   msg.message   of
                                wm_user:
                                begin
                                    //初始化发送消息字串
                                    asendmsg   :=   clientsendmsg;
                                    fillchar(conn,   sizeof(conn),   0);
                                    conn.len   :=   length(asendmsg);
                                    strpcopy(conn.content,   asendmsg);
                                    count   :=   sizeof(integer)   +   length(asendmsg);
                                    //发送自定义消息   如   登陆,注销等
                                    fsocket.sendbuf((@conn)^,   count);
                                end;
                                wm_close:
                                begin
                                    //发送客户端正常关闭消息
                                    asendmsg   :=   'oout$%^&';
                                    fillchar(conn,   sizeof(conn),   0);
                                    conn.len   :=   length(asendmsg);
                                    strpcopy(conn.content,   asendmsg);
                                    count   :=   sizeof(integer)   +   length(asendmsg);
                                    //正常退出,服务端会对数据库做相应操作
                                    fsocket.sendbuf((@conn)^,count);
                                    //退出线程
                                    break;
                                end;
                            end;
                        end;
                end;
                wait_timeout:
                begin
                    //线程启动时发送连接消息
                    if   hadsendconn   =   false   then
                    begin
                        fillchar(conn,   sizeof(conn),   0);
                        conn.len   :=   length(connstr);
                        strpcopy(conn.content,   connstr);
                        count   :=   sizeof(integer)   +   length(connstr);
                        fsocket.sendbuf((@conn)^,count);
                        //置已连接标志为真
                        hadsendconn   :=   true;
                    end;
                    ss   :=   formatdatetime('hh:mm:ss',(now()-lastrecvtest));
                    if   (now()-lastrecvtest)> 6/24*60   then
                    begin
                        break;
                    end;
                end;
                else
                begin
                    wsaresetevent(event);
                    //如果当前事件减去最后接收到测试消息的时间超过6分钟则线程退出
                    if   (now()-lastrecvtest)> 6/24*60   then
                    begin
                        break;
                    end;                
                end;
            end;
    finally
        wsacloseevent(event);
        try
        fsocket.close;
        except
              on   e:exception   do
                writeerrorlog('close   sokcet   error-> '+e.message);
            end;
    end;
end;
end.
发表于:2008-01-17 16:25:311楼 得分:0
请专家帮忙分析啊。谢谢。我只有100分全部奉上了。
发表于:2008-01-18 15:31:182楼 得分:0
100分都没有人要吗?????
发表于:2008-01-18 15:50:173楼 得分:100
请使用代码格式化,这样看着很累的。
楼主大部分都用api实现,那为什么还要用tclientwinsocket?这样子很难确实两者会不会产生冲突。造成事件或者状态的丢失。
发表于:2008-01-18 15:56:594楼 得分:0
另外clientrecvmsg的使用是有问题的。很可能造成前一个没有处理,后一个又过来覆盖了,假如网络通畅的时候,而界面上操作比较多,到达主线的消息比较多。
发表于:2008-01-18 15:57:355楼 得分:0
tthread的同步函数,可以用来显示。
发表于:2008-01-18 20:57:196楼 得分:0
谢谢僵哥。。
我的界面操作几乎没有。只有一个链接,登录和注销的动作。其余没有操作的。
还有clientrecvmsg使用我想不是很好。不过我不知道用什么办法把线程接受到的字符串往外传。
或许我只能在线程内部做分析,然后给主界面发一个刷新消息。

请师兄们再指点指点。拜。。。。

发表于:2008-01-18 23:58:067楼 得分:0
线程类添加一个私有(private)成员messagestring,再添加一个没有参数的私有方法,里面就只是执行显示界面的代码,信息从messagestring取,不需要临界区,只需要在线程当中调用该方法时使用synchronize.比如该方法叫做syncmessage,调用代码就是synchronize(syncmessage).
发表于:2008-01-19 21:05:108楼 得分:0
谢谢僵哥。。好的。我试一下。结贴了。谢谢。。。
发表于:2008-01-19 21:12:169楼 得分:0
谢谢


快速检索

最新资讯
热门点击