欢迎来到三一文库! | 帮助中心 三一文库31doc.com 一个上传文档投稿赚钱的网站
三一文库
全部分类
  • 研究报告>
  • 工作总结>
  • 合同范本>
  • 心得体会>
  • 工作报告>
  • 党团相关>
  • 幼儿/小学教育>
  • 高等教育>
  • 经济/贸易/财会>
  • 建筑/环境>
  • 金融/证券>
  • 医学/心理学>
  • ImageVerifierCode 换一换
    首页 三一文库 > 资源分类 > DOC文档下载  

    一个TCP非阻塞client端简单的例子.doc

    • 资源ID:3361852       资源大小:40KB        全文页数:9页
    • 资源格式: DOC        下载积分:4
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录 QQ登录   微博登录  
    二维码
    微信扫一扫登录
    下载资源需要4
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    一个TCP非阻塞client端简单的例子.doc

    一个TCP非阻塞client端简单的例子如果我们要产生一个非阻塞的socket,在C语言中如下代码所示:/ 创建socketintsock_fd = socket(AF_INET,SOCK_STREAM,0);./ 更改socket为nonblockfcntl(sock_fd,F_SETFL,fdflags | O_NONBLOCK);/ connect.while(1)    intrecvlen = recv(sock_fd,recvbuf,RECV_BUF_SIZE);.由于网络协议非常复杂,内核里面用到了大量的面向对象的技巧,所以我们从创建连接开始,一步一步追述到最后代码的调用点。socket的创建很明显,内核的第一步应该是通过AF_INET、SOCK_STREAM以及最后一个参数0定位到需要创建一个TCP的socket,如下图绿线所示:我们跟踪源码调用socket(AF_INET,SOCK_STREAM,0)|->sys_socket进入系统调用|->sock_create|->_sock_create进一步分析_sock_create的代码判断:conststructnet_proto_family *pf;/ RCU(Read-Copy Update)是linux的一种内核同步方法,在此不阐述/ family=INETpf = rcu_dereference(net_familiesfamily);err = pf->create(net,sock,protocol);const struct net_proto_family *pf; / RCU(Read-Copy Update)是linux的一种内核同步方法,在此不阐述 / family=INET pf = rcu_dereference(net_familiesfamily); err = pf->create(net, sock, protocol);则通过源码可知,由于是AF_INET(PF_INET),所以net_familiesPF_INET.create=inet_create(以后我们都用PF_INET表示),即pf->create = inet_create; 进一步追溯调用:inet_create(structnet *net,structsocket *sock,intprotocol)Sock* sock;./ 此处是寻找对应协议处理器的过程lookup_protocol:/ 迭代寻找protocol=answer->protocol的情况list_for_each_rcu(p, /* Check the non-wild match. */if(protocol = answer->protocol)if(protocol != IPPROTO_IP)break;./ 这边answer指的是SOCK_STREAMsock->ops = answer->ops;answer_no_check = answer->no_check;/ 这边sk->prot就是answer_prot=>tcp_protsk = sk_alloc(net,PF_INET,GFP_KERNEL,answer_prot);sock_init_data(sock,sk);.上面的代码就是在INET中寻找SOCK_STREAM的过程了 我们再看一下inetswSOCK_STREAM的具体配置:staticstructinet_protosw inetsw_array =.type =       SOCK_STREAM,.protocol =   IPPROTO_TCP,.prot =       ">.ops =        ">.capability = -1,.no_check =   0,.flags =      INET_PROTOSW_PERMANENT |INET_PROTOSW_ICSK,.这边也用了重载,AF_INET有TCP、UDP以及Raw三种:从上述代码,我们可以清楚的发现sock->ops=conststructproto_ops inet_stream_ops = .family    = PF_INET,.owner    = THIS_MODULE,.sendmsg    = tcp_sendmsg,.recvmsg    = sock_common_recvmsg,.即sock->ops->recvmsg = sock_common_recvmsg;同时sock->sk->sk_prot = tcp_prot;我们再看下tcp_prot中的各个函数重载的定义:structproto tcp_prot = .name = "TCP",.close = tcp_close,.connect = tcp_v4_connect,.disconnect = tcp_disconnect,.accept = inet_csk_accept,./ 我们重点考察tcp的读.recvmsg = tcp_recvmsg,.fcntl控制socket的阻塞非阻塞状态我们用fcntl修改socket的阻塞非阻塞状态。 事实上: fcntl的作用就是将O_NONBLOCK标志位存储在sock_fd对应的filp结构的f_lags里,如下图所示。fcntl(sock_fd,F_SETFL,fdflags | O_NONBLOCK);|->setfl追踪setfl代码:staticintsetfl(intfd,structfile * filp,unsignedlongarg).filp->f_flags = (arg .上图中,由sock_fd在task_struct(进程结构体)->files_struct->fd_array中找到对应的socket的file描述符,再修改file->flags在调用socket.recv的时候我们跟踪源码调用:socket.recv|->sys_recv|->sys_recvfrom|->sock_recvmsg|->_sock_recvmsg|->sock->ops->recvmsg由上文可知: sock->ops->recvmsg = sock_common_recvmsg;sock值得注意的是,在sock_recmsg中,有对标识O_NONBLOCK的处理if(sock->file->f_flags ">flags |= MSG_DONTWAIT;上述代码中sock关联的file中获取其f_flags,如果flags有O_NONBLOCK标识,那么就设置msg_flags为MSG_DONTWAIT(不等待)。fcntl与socket就是通过其共同操作File结构关联起来的。继续跟踪调用sock_common_recvmsgintsock_common_recvmsg(structkiocb *iocb,structsocket *sock,structmsghdr *msg,size_t size,intflags)./ 如果flags的MSG_DONTWAIT标识置位,则传给recvmsg的第5个参数为正,否则为0err = sk->sk_prot->recvmsg(iocb,sk,msg,size,flags ">flags .   由上文可知: sk->sk_prot->recvmsg 其中sk_prot=tcp_prot,即最终调用的是tcp_prot->tcp_recvmsg,上面的代码可以看出,如果fcntl(O_NONBLOCK)=>MSG_DONTWAIT置位=>(flags ">最终的调用逻辑tcp_recvmsg首先我们看下tcp_recvmsg的函数签名:inttcp_recvmsg(structkiocb *iocb,structsock *sk,structmsghdr *msg,size_t len,intnonblock,intflags,int *addr_len)显然我们关注焦点在(int nonblock这个参数上):inttcp_recvmsg(structkiocb *iocb,structsock *sk,structmsghdr *msg,size_t len,intnonblock,intflags,int *addr_len)./ copied是指向用户空间拷贝了多少字节,即读了多少intcopied;/ target指的是期望多少字节inttarget;/ 等效为timo = noblock ? 0 : sk->sk_rcvtimeo;timeo = sock_rcvtimeo(sk,nonblock);./ 如果设置了MSG_WAITALL标识target=需要读的长度/ 如果未设置,则为最低低水位值target = sock_rcvlowat(sk,flags .do/ 表明读到数据if(copied)/ 注意,这边只要!timeo,即nonblock设置了就会跳出循环if(sk->sk_err |sk->sk_state = TCP_CLOSE |(sk->sk_shutdown ">!timeo |signal_pending(current) |(flags ">break;else/ 到这里,表明没有读到任何数据/ 且nonblock设置了导致timeo=0,则返回-EAGAIN,符合我们的预期if(!timeo)copied = -EAGAIN;break;/ 这边如果堵到了期望的数据,继续,否则当前进程阻塞在sk_wait_data上if(copied >= target)/* Do not sleep, just process backlog. */release_sock(sk);lock_sock(sk);elsesk_wait_data(sk, while(len > 0);.returncopied上面的逻辑归结起来就是:(1)在设置了nonblock的时候,如果copied>0,则返回读了多少字节,如果copied=0,则返回-EAGAIN,提示应用重复调用。(2)如果没有设置nonblock,如果读取的数据>=期望,则返回读取了多少字节。如果没有则用sk_wait_data将当前进程等待。如下流程图所示:阻塞函数sk_wait_datask_wait_data代码-函数为:/ 将进程状态设置为可打断INTERRUPTIBLEprepare_to_wait(sk->sk_sleep, set_bit(SOCK_ASYNC_WAITDATA, / 通过调用schedule_timeout让出CPU,然后进行睡眠rc = sk_wait_event(sk,timeo, !skb_queue_empty(/ 到这里的时候,有网络事件或超时事件唤醒了此进程,继续运行clear_bit(SOCK_ASYNC_WAITDATA, finish_wait(sk->sk_sleep, 该函数调用schedule_timeout进入睡眠,其进一步调用了schedule函数,首先从运行队列删除,其次加入到等待队列,最后调用和体系结构相关的switch_to宏来完成进程间的切换。如下图所示:阻塞后什么时候恢复运行呢情况1:有对应的网络数据到来首先我们看下网络分组到来的内核路径,网卡发起中断后调用netif_rx将事件挂入CPU的等待队列,并唤起软中断(soft_irq),再通过linux的软中断机制调用net_rx_action,如下图所示:紧接着跟踪next_rx_actionnext_rx_action|-process_backlog.|->packet_type->func在这里我们考虑ip_rcv|->ipprot->handler在这里ipprot重载为tcp_protocol(handler即为tcp_v4_rcv)紧接着tcp_v4_rcv:tcp_input.ctcp_v4_rcv|-tcp_v4_do_rcv|-tcp_rcv_state_process|-tcp_data_queue|-sk->sk_data_ready=sock_def_readable|-wake_up_interruptible|-_wake_up|-_wake_up_common在这里_wake_up_common将停在当前wait_queue_head_t中的进程唤醒,即状态改为task_running,等待CFS调度以进行下一步的动作,如下图所示。情况2:设定的超时时间到来在前面调用sk_wait_event中调用了schedule_timeoutfastcall signedlong_sched schedule_timeout(signedlongtimeout)./ 设定超时的回掉函数为process_timeoutsetup_timer(_mod_timer(/ 这边让出CPUschedule();del_singleshot_timer_sync(timeout = expire - jiffies;out:/ 返回经过了多长事件returntimeout < 0?0 : timeout;process_timeout函数即是将此进程重新唤醒staticvoidprocess_timeout(unsignedlong_data)wake_up_process(structtask_struct *)_data);

    注意事项

    本文(一个TCP非阻塞client端简单的例子.doc)为本站会员(白大夫)主动上传,三一文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一文库(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    经营许可证编号:宁ICP备18001539号-1

    三一文库
    收起
    展开