网络编程常见问题总结

时间:2019-05-12 03:57:18下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《网络编程常见问题总结》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《网络编程常见问题总结》。

第一篇:网络编程常见问题总结

网络编程常见问题总结

网络编程常见问题总结 串讲(一)

网络编程常见问题总结

在网络程序中遇到的一些问题进行了总结, 这里主要针对的是我们常用的TCP socket相关的总结,可能会存在错误,有任何问题欢迎大家提出.对于网络编程的更多详细说明建议参考下面的书籍

《UNIX网络编程》 《TCP/IP 详解》 《Unix环境高级编程》

非阻塞IO和阻塞IO:

在网络编程中对于一个网络句柄会遇到阻塞IO和非阻塞IO的概念, 这里对于这两种socket先做一下说明

基本概念:

socket的阻塞模式意味着必须要做完IO操作(包括错误)才会返回。非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功。

设置:

一般对于一个socket是阻塞模式还是非阻塞模式有两种方式 fcntl设置和recv,send系列的参数.fcntl函数可以将一个socket句柄设置成非阻塞模式:

flags = fcntl(sockfd, F_GETFL, 0);fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);设置之后每次的对于sockfd的操作都是非阻塞的

recv, send函数的最后有一个flag参数可以设置成MSG_DONTWAIT临时将sockfd设置为非阻塞模式,而无论原有是阻塞还是非阻塞。recv(sockfd, buff, buff_size, MSG_DONTWAIT);send(scokfd, buff, buff_size, MSG_DONTWAIT);区别:

读:

读本质来说其实不能是读,在实际中, 具体的接收数据不是由这些调用来进行,是由于系统底层自动完成的,read也好,recv也好只负责把数据从底层缓冲copy到我们指定的位置.对于读来说(read, 或者 recv),在阻塞条件下如果没有发现数据在网络缓冲中会一直等待,当发现有数据的时候会把数据读到用户指定的缓冲区,但是如果这个时候读到的数据量比较少,比参数中指定的长度要小,read并不会一直等待下去,而是立刻返回。read的原则是数据在不超过指定的长度的时候有多少读多少,没有数据就会一直等待。所以一般情况下我们读取数据都需要采用循环读的方式读取数据,一次read完毕不能保证读到我们需要长度的数据,read完一次需要判断读到的数据长度再决定是否还需要再次读取。在非阻塞的情况下,read的行为是如果发现没有数据就直接返回,如果发现有数据那么也是采用有多少读多少的进行处理.对于读而言,阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.

recv中有一个 MSG_WAITALL的参数 recv(sockfd, buff, buff_size, MSG_WAITALL), 在正常情况下 recv是会等待直到读取到buff_size长度的数据,但是这里的WAITALL也只是尽量读全,在有中断的情况下recv还是可能会 被打断,造成没有读完指定的buff_size的长度。所以即使是采用recv + WAITALL参数还是要考虑是否需要循环读取的问题,在实验中对于多数情况下recv还是可以读完buff_size,所以相应的性能会比直接read 进行循环读要好一些。不过要注意的是这个时候的sockfd必须是处于阻塞模式下,否则WAITALL不能起作用。

写:

写的本质也不是进行发送操作,而是把用户态的数据copy到系统底层去,然后再由系统进行发送操作,返回成功只表示数据已经copy到底层缓冲,而不表示数据以及发出,更不能表示对端已经接收到数据.对于write(或 者send)而言,在阻塞的情况是会一直等待直到write完全部的数据再返回.这点行为上与读操作有 所不同,究其原因主要是读数据的时候我们并不知道对端到底有没有数据,数据是在什么时候结束发送的,如果一直等待就可能会造成死循环,所以并没有去进行这方面的处理;而对于write, 由于需要写的长度是已知的,所以可以一直再写,直到写完.不过问题是write是可能被打断造成write一次只write一部分数据, 所以write的过程还是需要考虑循环write, 只不过多数情况下一次write调用就可能成功.非阻塞写的情况下,是采用可以写多少就写多少的策略.与读不一样的地方在于,有多少读多少是由网络发送的那一端是否有数据传输到为标准,但是对于可以写多少是由本地的网络堵塞情况为标准的,在网络阻塞严重的时候,网络层没有足够的内存来进行写操作,这时候就会出现写不成功的情况,阻塞情况下会尽可能(有可能被中断)等待到数据全部发送完毕,对于非阻塞的情况就是一次写多少算多少,没有中断的情况下也还是会出现write到一部分的情况.网络编程常见问题总结 串讲(二)

超时控制:

对于网络IO,我们一般情况下都需要超时机制来避免进行操作的线程被handle住,经典的做法就是采用select+非阻塞IO进行判断,select在超时时间内判断是否可以读写操作,然后采用非堵塞读写,不过一般实现的时候读操作不需要设置为非堵塞,上面已经说过读操作只有在没有数据的 时候才会阻塞,select的判断成功说明存在数据,所以即使是阻塞读在这种情况下也是可以做到非阻塞的效果,就没有必要设置成非阻塞的情况了.

这部分的代码可以参考ullib中ul_sreado_ms_ex和ul_swriteo_ms_ex.

采用ul_sreado_ms_ex读数据也是不能保证返回大于0就一定读到指定的数据长度, 对于读写操作, 都是需要判断返回的读长度或者写长度是否是需要的长度, 不能简单的判断一下返回值是否小于0.对于ul_sreado_ms_ex的情况如果出现了发送端数据发送一半就被close掉的情况就有可能导致接收端读不到完整的数据包.errno 只有在函数返回值为负的时候才有效,如果返回0或者大于0的数, errno 的结果是无意义的.有些时候 会出现read到0,但是我们认为是错误的情况然后输出errno造成误解,一般建议在这种情况要同时输出返回值和errno的结果,有些情况由于只有errno造成了对于问 题的判断失误。

长连接和短连接的各种可能的问题及相应的处理

这里主要是发起连接的客户端的问题,这里列出的问题主要是在采用同步模型的情况下才会存在的问题.短连接:

采用短连接的情况一般是考虑到下面的一些问题: 后端服务的问题, 考虑最简单的情况下一个线程一个连接, 如果这个连接采用了长连接那么就需要我们处理连接的线程和后端保持一一对应,然后按照某些原则进行处理(n对n的关系), 但由于一方面服务器可能增加,这样导致需要前后端保持一致,带来了更多的麻烦,另一方面线程数上不去对应处理能力也会产生影响,而短连接每次连接的时候只 需要关注当前的机器,问题相对会少一些.其实这个问题可以采用连接池的方式来解决,后面会提到.不需要考虑由于异常带来的脏数据。负载均衡方面可以简单考虑, 无论线程数是多少还是后端服务器的数量是多少都没有关系, 每次考虑单个连接就可以了.当然如果负载逻辑简单,并且机器相对固定,一个线程一个长连接问题也不大.规避一些问题, 在过去有些情况下出现长连接大延时,数据没响应等问题, 测试的时候发现换短连接问题就解决了,由于时间关系就没有再继续追查, 事实上这些问题现在基本上都已经定位并且有相关的解决方案了.不足:

效率不足, 由于连接操作一般会有50ns~200ns的时间消耗,导致短连接需要消耗更多的时间会产生TIME_WAIT问题,需要做更多的守护

长连接:

长连接相比短连接减少了连接的时间消耗, 可以承受更高的负载.但在使用的时候需要考虑一些问题脏数据, 在一些特殊情况(特别是逻辑错误的情况下)会存在一些我们并不需要的数据.这个时候的处理比较安全的方式是一旦检测到就关闭连接, 检测的方式在在发起请求前用前面为什么socket写错误,但用recv检查依然成功? 介绍的方式进行检查.不过有些程序会采用继续读把所有不需要的数据读完毕(读到 EAEGIN), 不过这种方式过分依赖逻辑了,存在了一定的风险.不如直接断开来的简单 后端连接, 前面也提到了 在这种情况我们一般会采用连接池的方式来解决问题比如(public/connectpool中就可以维护不同的连接,使每个线程都可以均匀的获取到句 柄)服务端的处理这个时候需要考虑连接的数量,简单的方式就是一个长连接一个线程, 但是线程也不能无限增加(增加了,可能造成大量的上下文切换使的性能下降).我们一般在长连接的情况采用pendingpool的模型, 通过一个异步队列来缓冲, 这样不需要考虑客户端和服务端的线程数问题,可以任意配置(可以通过线下测试选择合适的线程数)

一些特殊的问题, 主要是长连接的延时 在后面的FAQ中会有详细的说明.一般来说,对于我们多数的内部业务逻辑都是可以采用长连接模式,不会产生太多的问题.网络编程常见问题总结 串讲(三)

主要线程模型优缺点和注意事项

这里所列出的线程模型,目前在我们的public/ub下都有相关的实现,在 ubFAQ中也有相关的说明,这里主要针对这些模 型的使用做相关的说明

最简单的线程模型 同时启动多个线程,每个线程都采用accept的方式进行阻塞获取连接(具体实现上一般是先select在accept, 一方面规避低内核的惊群效应,另一方面可以做到优雅退出).多个线程竞争一个连接, 拿到连接的线程就进行自己的逻辑处理, 包括读写IO全部都在一个线程中进行.短连接每次重新accept, 长连接,第一次的时候accept然后反复使用.一般来说在总连接数很少的情况下效果会比较好,相对适用于少量短连接(可以允许比线程数多一些)和不超过线程总数的长连接(超过的那些连接,除非 accept的连接断开,否则不可能会有线程对它进行accept).但如果同一时候连接数过多会造成没有工作线程与客户端进行连接,客户端会出现大量的连接失败, 因为这个时候线程可能存在不能及时accept造成超时问题, 在有重试机制的情况下可能导致问题更糟糕.有些程序在出现几次超时之后会长时间一直有连接超时往往就是在这种情况下发生的.这种模型的最大优点在于编写简单, 在正常情况下工作效果不错.在public/ub中的xpool就是属于这种模型,建议针对连接数少的服务进行使用,比如一些一对一的业务逻辑.生产者消费者模型

普通线程模型在长连接方面存在使用限制(需要对于线程数进行变化, 而线程又不是无限的), 短连接在处理同时大量连接(比如流量高峰期)的时候存在问题.生产者消费者模型是可以把这种影响减少.对于有数据的活动连接放到异步队列中, 其他线程竞争这个队列获取句柄然后进行相关的操作.由于accept是专门的线程进行处理, 出现被handle的情况比较少,不容易出现连接失败的情况.在大流量的情况下有一定的缓冲,虽然有些请求会出现延时,但只要在可以接受的范围内,服务还 是可以正常进行.一般来说队列的长度主要是考虑可以接受的延时程度.这种模式也是我们现在许多服务比较常用的模型.可以不用关心客户端和服务的线程数对应关系,业务逻辑上也是比较简单的。

但这种模式在编程的 时候,对于长连接有一个陷阱,判断句柄是否可读写以前一般采用的是select, 如果长连接的连接数比工作线程还少,当所有的连接都被处理了,有连接需要放回pool中,而这个时候如果正常建立连接的监听线程正好处于select状 态,这个时候必须要等到 select超时才能重新将连接放入select中进行监听,因为这之前被放入select进行监听的处理socket为空,不会有响应,这个时候由于时 间的浪费造成l长连接的性能下降。一般来说某个连接数少,某个连接特别活跃就可能造成问题.过去的一些做法是控制连接数和服务端的工作线程数以及通过监听一个管道fd,在工作线程结束每次都激活这个fd跳出这次select来控制。现在的2.6 内核中的epoll在判断可读写的时候不会存在这个问题(epoll在进行监听的时候,其它线程放入或者更改, 在epoll_wait的时候是可以马上激活的), 我们现在的服务多采用epoll代替select来解决这个, 但是主要的逻辑没有变化.ub_server中epool和public/ependingpool都是采用种模式

异步模型

这里只做一些简单的介绍。上 面两者模型本质都是同步的处理业务逻辑,在一个线程中处理了读请求,业务逻辑和写回响应三个过程(很多业务更复杂,但是都是可以做相应的拆封的), 但是读和写这两个IO的处理往往需要阻塞等待, 这样造成了线程被阻塞, 如果要应付慢连接(比如外围抓取等待的时间是秒级的甚至更多), 在等待的时候其实CPU没有干多少事情, 这个时候就造成了浪费.一种考虑是增加线程数,通过提高并发来解决这个问题, 但是我们目前的线程数还是有限的,不可能无限增加.而且线程的增加会带来cpu对于上下文切换的代价,另一方面多个线程从一个队列中获取可用连接, 这里存在互斥线程多的时候会导致性能下降,当然这里可以通过把一个队列改多队列减少互斥来实现.引入异步化的处理, 就是把对于IO的等待采用IO复用的方式,专门放入到一个或者若干个线程中去, 处理主逻辑的程序可以被释放出来, 只有在IO处理完毕才进行处理, 这样可以提高CPU的使用率,减少等待的时间.一般情况下几个线程(一般和CPU的核数相当)可以应付很大的流量请求 public/kylin , ub/ub(ub事件模型)都是基于纯异步思想的异步框架。而ub中的appool是简化版本将原本ub框架中网络IO处理进行了异步化,不过目前只支持 采用nshead头的模式。网络编程常见问题总结 串讲(四)

为什么网络程序会没有任何预兆的就退出了

一般情况都是没有设置忽略PIPE信号,在我们的环境中当网络触发broken pipe(一般情况是write的时候,没有write完毕,接受端异常断开了),系统默认的行为是直接退出。在我们的程序中一般都要在启动的时候加上 signal(SIGPIPE, SIG_IGN);来强制忽略这种错误

write出去的数据, read的时候知道长度吗?

严格来说, 交互的两端, 一端write调用write出去的长度, 接收端是不知道具体要读多长的.这里有几个方面的问题

write 长度为n的数据, 一次write不一定能成功(虽然小数据绝大多数都会成功), 需要循环多次write,write虽然成功,但是在网络中还是可能需要拆包和组包, write出来的一块数据, 在接收端底层接收的时候可能早就拆成一片一片的多个数据包.TCP层中对于接收到的数据都是把它们放到缓冲中, 然后read的时候一次性copy, 这个时候是不区分一次write还是多次write的。所以对于网络传输中 我们不能通过简单的read调用知道发送端在这次交互中实际传了多少数据.一般来说对于具体的交互我们一般采取下面的方式来保证交互的正确,事先约定好长度, 双方都采用固定长度的数据进行交互, read, write的时候都是读取固定的长度.但是这样的话升级就必须考虑两端同时升级的问题。特殊的结束符或者约定结束方式, 比如http头中采用连续的/r/n来做头部的结束标志.也有一些采用的是短连接的方式, 在read到0的时候,传输变长数据的时候一般采用定长头部+变长数据的方式, 这个时候在定长的头部会有一个字段来表示后面的变长数据的长度, 这种模式下一般需要读取两次确定长度的数据.我们现在内部用的很多都是这样的模式.比如public/nshead就是这样处理, 不过nshead作为通用库另外考虑了采用 通用定长头+用户自定义头+变长数据的接口。

总的来说read读数 据的时候不能只通过read的返回值来判断到底需要读多少数据, 我们需要额外的约定来支持, 当这种约定存在错误的时候我们就可以认为已经出现了问题.另外对于write数据来说, 如果相应的数据都是已经准备好了那这个时候也是可以把数据一次性发送出去,不需要调用了多次write.一般来说write次数过多也会对性能产生影响,另一个问题就是多次连续可能会产生延时问题,这个参看下面有关长连接延时的部分问题.小提示

上面提到的都是TCP的情况, 不一定适合其他网络协议.比如在UDP中 接收到连续2个UDP包, 需要分别读来次才读的出来, 不能像TCP那样,一个read可能就可以成功(假设buff长度都是足够的)。如何查看和观察句柄泄露问题 一般情况句柄只有1024个可以使用,所以一般情况下比较容易出现,也可以通过观察/proc/进程号/fd来观察。

另外可以采用valgrind来检查,valgrind参数中加上--track-fds = yes 就可以看到最后退出的时候没有被关闭的句柄,以及打开句柄的位置

为什么socket写错误,但用recv检查依然成功?

首先采用recv检查连接的是基于我们目前的一个请求一个应答的情况对于客户端的请求,逻辑一般是这样 建立连接->发起请求->接受应答->长连接继续发请求

recv检查一般是这样采用下面的方式: ret = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);通过判断ret 是否为-1并且errno是EAGAIN 在非堵塞方式下如果这个时候网络没有收到数据, 这个时候认为网络是正常的。这是由于在网络交换模式下 我们作为一个客户端在发起请求前, 网络中是不应该存在上一次请求留下来的脏数据或者被服务端主动断开(服务端主动断开会收到FIN包,这个时候是recv返回值为0), 异常断开会返回错误.当然这种方式来判断连接是否存在并不是非常完善,在特殊的交互模式(比如异步全双工模式)或者延时比较大的网络中都是存在问题的,不过对于我们目前内网中的交互模式还是基本适用的.这种方式和socket写错误并不矛盾, 写数据超时可能是由于网慢或者数据量太大等问题, 这时候并不能说明socket有错误, recv检查完全可能会是正确的.一般来说遇到socket错误,无论是写错误还读错误都是需要关闭重连.为什么接收端失败,但客户端仍然是write成功

这个是正常现象,write数据成功不能表示数据已经被接收端接收导致,只能表示数据已经被复制到系统底层的缓冲(不一定发出), 这个时候的网络异常都是会造成接收端接收失败的.长连接的情况下出现了不同程度的 延时 在一些长连接的条件下, 发送一个小的数据包,结果会发现从数据write成功到接收端需要等待一定的时间后才能接收到, 而改成短连接这个现象就消失了(如果没有消失,那么可能网络本身确实存在延时的问题,特别是跨机房的情况下)在长连接的处理中出现了延时,而且时间固定,基本都是40ms, 出现40ms延时最大的可能就是由于没有设置TCP_NODELAY 在长连接的交互中,有些时候一个发送的数据包非常的小,加上一个数据包的头部就会导致浪费,而且由于传输的数据多了,就可能会造成网络拥塞的情况, 在系统底层默认采用了Nagle算法,可以把连续发送的多个小包组装为一个更大的数据包然后再进行发送.但是对于我们交互性的应用程序意义就不大了,在这种情况下我们发送一个小数据包的请求,就会立刻进行等待,不会还有后面的数据包一起发送, 这个时候Nagle算法就会产生负作用,在我们的环境下会产生40ms的延时,这样就会导致客户端的处理等待时间过长, 导致程序压力无法上去.在代码中无论是服务端还是客户端都是建议设置这个选项,避免某一端造成延时。所以对于长连接的情况我们建议都需要设置TCP_NODELAY, 在我们的ub框架下这个选项是默认设置的.小提示:

对于服务端程序而言, 采用的模式一般是

bind-> listen-> accept, 这个时候accept出来的句柄的各项属性其实是从listen的句柄中继承, 所以对于多数服务端程序只需要对于listen进行监听的句柄设置一次TCP_NODELAY就可以了,不需要每次都accept一次.设置了NODELAY选项但还是时不时出现10ms(或者某个固定值)的延时 这种情况最有可能的就是服务端程序存在长连接处理的缺陷.这种情况一般会发生在使用我们的pendingpool模型(ub中的cpool)情况下,在 模型的说明中有提到.由于select没有及时跳出导致一直在浪费时间进行等待.上面的2个问题都处理了,还是发现了40ms延时?

协议栈在发送包的时候,其实不仅受到TCP_NODELAY的影响,还受到协议栈里面拥塞窗口大小的影响.在连接发送多个小数据包的时候会导致数据没有及时发送出去.这里的40ms延时其实是两方面的问题: 对于发送端, 由于拥塞窗口的存在,在TCP_NODELAY的情况,如果存在多个数据包,后面的数据包可能会有延时发出的问题.这个时候可以采用 TCP_CORK参数, TCP_CORK 需要在数据write前设置,并且在write完之后取消,这样可以把write的数据发送出去(要注意设置TCP_CORK的时候不能与TCP_NODELAY混用,要么不设置TCP_NODELAY要么就先取消TCP_NODELAY)但是在做了上 面的设置后可能还是会导致40ms的延时, 这个时候如果采用tcpdump查看可以注意是发送端在发送了数据包后,需要等待服务端的一个ack后才会再次发送下一个数据包,这个时候服务端出现了延 时返回的问题.对于这个问题可以通过设置server端TCP_QUICKACK选项来解决.TCP_QUICKACK可以让服务端尽快的响应这个ack包.这个问题的主要原因比较复杂,主要有下面几个方面

当TCP协议栈收到数据的时候, 是否进行ACK响应(没有响应是不会发下一个包的),在我们linux上返回ack包是下面这些条件中的一个接收的数据足够多

处于快速回复模式(TCP_QUICKACK)

存在乱序的包,如果有数据马上返回给发送端,ACK也会一起跟着发送

如果都不满足上面的条件,接收方会延时40ms再发送ACK,这个时候就造成了延时。

但是对于上面的情况即使是采用TCP_QUICKACK,服务端也不能保证可以及时返回ack包,因为快速回复模式在一些情况下是会失效(只能通过修改内核来实现)目前的解决方案只能是通过修改内核来解决这个问题,STL的同学在 内核中增加了参数可以控制这个问题。

会出现这种情况的主要是连接发送多个小数据包或者采用了一些异步双工的编程模式,主要的解决方案有下面几种对于连续的多个小数据包,尽量把他们打到一个buffer中间, 不过会有内存复制的问题(采用writev方式发送多个小数据包,不过writev也存在一个问题就是发送的数据包个数有限制,如果超过了IOV_MAX(我们的限制一般是1024),依然可能会出现问题,因为writev只能保证在IOV_MAX范围内的数据是按照连续发送的。

writev或者大buffer的方式在异步双工模式下是无法工作,这个时候只能通过系统方式来解决。客户端 不设置TCP_NODELAY选项,发送数据前先打开TCP_CORK选项,发送完后再关闭TCP_CORK,服务端开启TCP_QUICKACK选项

采用STL修改的内核5-6-0-0,打开相关参数。

网络编程常见问题总结 串讲(五)

TIME_WAIT有什么样的影响?

对于TIME_WAIT的出现具体可以参考<>中的章节,总的来说对于一个已经建立的连接如果是主动 close, 那么这个连接的端口(注意:不是socket)就会进入到TIME_WAIT状态,在我们的机器上需要等待60s的时间(有些书上可能提到的是 2MSL,1MSL为1分钟,但我们的linux实现是按照1分钟的).在这一段时间内,这个端口将不会被释放,新建立的连接就无法使用这个端口(连接的时候会报Cannot assign requested address的错误).可以通过/proc/sys/net/ipv4/ip_local_port_range看到可用端口的范围,我们的机器上一般是32768--61000,不足3W个,这样的结果就是导致如果出现500/s的短连接请求,就会导致端口不够用连接不上。这种情况一般修改系统参数tcp_tw_reuse或者在句柄关闭前设置SO_LINGER选项来解决,也可以通过增大 ip_local_port_range来缓解,设置SO_LINGER后句柄会被系统立刻关闭,不会进入TIME_WAIT状态,不过在一些大压力的情况还是有可能出现连接的替身,导致数据包丢失。系统参数/proc/sys/net/ipv4/tcp_tw_reuse设为1 会复用TIME_WAIT状态socket,如果开启,客户端在调用connect调用时,会自动复用TIME_WAIT状态的端口,相比 SO_LINGER选项更加安全。

对于服务器端如果出现TIME_WAIT状态,是不会产生端口不够用的情况,但是TIME_WAIT过多在服务器端还是会占用一定的内存资源,在/proc/sys/net/ipv4/tcp_max_xxx 中我们可以系统默认情况下的所允许的最大TIME_WAIT的个数,一般机器上都是180000, 这个对于应付一般程序已经足够了.但对于一些压力非常大的程序而言,这个时候系统会不主动进入TIME_WAIT状态而且是直接跳过,这个时候如果去看 dmsg中的信息会看到 “TCP: time wait bucket table overflow” , 一般来说这种情况是不会产生太多的负面影响, 这种情况下后来的socket在关闭时不会进入TIME_WAIT状态,而是直接发RST包, 并且关闭socket.不过还是需要关注为什么会短时间内出现这么大量的请求。

小提示: 如果需要设置SO_LINGER选项, 需要在FD连接上之后设置才有效果

: O% B: j3 o/ A

什么情况下会出现CLOSE_WAIT状态?

一般来说,连接的一端在被动关闭的情况下,已经接收到FIN包(对端调用close)后,这个时候如果接收到FIN包的一端没有主动close就会出 现CLOSE_WAIT的情况。一般来说,对于普通正常的交互,处于CLOSE_WAIT的时间很短,一般的逻辑是检测到网络出错,马上关闭。但是在一些情况下会出现大量的CLOS_WAIT, 有的甚至维持很长的时间,这个主要有几个原因:

没有正确处理网络异常,特别是read 0的情况,一般来说被动关闭的时候会出现read 返回0的情况。一般的处理的方式在网络异常的情况下就主动关闭连接句柄泄露了,句柄泄露需要关闭的连接没有关闭而对端又主动断开的情况下也会出现这样的问 题。连接端采用了连接池技术,同时维护了较多的长连接(比如ub_client, public/connectpool),同时服务端对于空闲的连接在一定的时间内会主动断开(比如ub_server, ependingpool都有这样的机制).如果服务端由于超时或者异常主动断开,客户端如果没有连接检查的机制,不会主动关闭这个连接,比如ub_client的机制就是长连接建立后除非到使用的时候进行连接检查,否则不会主动断开连接。这个时候在建立连接的一端就会出现CLOSE_WAIT状态。这个时候的状态一般来说是安全(可控的,不会超过最大连接数).在com 的connectpool 2中这种情况下可以通过打开健康检查线程进行主动检查,发现断开后主动close.网络编程常见问题总结 串讲(六)顺序发送数据,接收端出现乱序接收到的情况:

网络压力大的情况下,有时候会出现,发送端是按照顺序发送, 但是接收端接收的时候顺序不对.一般来说在正常情况下是不会出现数据顺序错误的情况, 但某些异常情况还是有可能导致的.在我们的协议栈中,服务端每次建立连接其实都是从accpet所在的队列中取出一个已经建立的fd, 但是在一些异常情况下,可能会出现短时间内建立大量连接的情况, accept的队列长度是有限制,这里其实有两个队列,一个完成队列另一个是未完成队列,只有完成了三次握手的连接会放到完成队列中。如果在短时间内accept中的fd没有被取出导致队 列变满,但未完成队列未满,这个时候连接会在未完成队列中,对于发起连接的一端来说表现的情况是连接已经成功,但实际上连接本身并没有完成,但这个时候我们依然可以发起写操作并且成 功,只是在进行读操作的时候,由于对端没有响应会造成读超时。对于超时的情况我们一般就把连接直接close关闭了,但是句柄虽然被关闭了,但是由于TIME_WAIT状态的存在,TCP还是会进行重传。在重传的时候,如果完成队列有句柄被处理,那么此时会完成三次握手建立连接,这个时候服务端照样会进行正常的处理(不过在写响应的 时候可能会发生错误)。从接收上看,由于重传成功的情况我们不能控制,对于接收端来说就可能出现乱序的情况。完成队列的长度和未完成队列的长度由listen时候的baklog决定((ullib库中ul_tcplisten的最后一个参数),在我们的 linux环境中baklog是完成队列的长度,baklog * 1.5是两个队列的总长度(与一些书上所说的两个队列长度不超过baklog有出入).两个队列的总长度最大值限制是128, 既使设置的结果超过了128也会被自动改为128。128这个限制可以通过 系统参数 /proc/sys/net/core/somaxconn 来更改, 在我们 5-6-0-0 内核版本以后,STL将其提高到2048.另外客户端也可以考虑使用SO_LINGER参数通过强制关闭连接来处理这个问题,这样在close以后就不启用重传机制。另外的考虑就是对重试机制根据 业务逻辑进行改进。

连接偶尔出现超时有哪些可能?

主要几个方面的可能

服务端确实处理能力有限,cpu idel太低, 无法承受这样的压力,或者 是更后端产生问题

accept队列设置过小,而连接又特别多,需要增大baklog,建议设置为128这是我们linux系统默认的最大值 由/proc/sys/net/core/somaxconn决定,可以通过修改这个值来增大(由于很多书上这个地方设置为5,那个其实是4.2BSD支 持的最大值, 而不是现在的系统, 不少程序中都直接写5了,其实可以更大, 不过超过128还是按照128来算)

程序逻辑问题导致accept处理不过来, 导致连接队列中的连接不断增多直到把accept队列撑爆, 像简单的线程模型(每个线程一个accept), 线程被其他IO一类耗时操作handle,导致accept队列被撑爆, 这个时候默认的逻辑是服务端丢弃数据包,导致client端出现超时,但是可以通过打开/proc/sys/net/ipv4/tcp_abort_on_overflow开关让服务端立刻返回失败

当读超时的时候(或者其他异常), 我们都会把连接关闭,进行重新连接,这样的行为如果很多,也可能造成accept处理不过来

异常情况下,设置了SO_LINGER造成连接的ack包被丢失, 虽然情况极少,但大压力下还是有存在的.当然还是有可能是由于网络异常或者跨机房耗时特别多产生的, 这些就不是用户态程序可以控制的。

另外还有发现有些程序采用epoll的单线模式, 但是IO并没有异步化,而是阻塞IO,导致了处理不及时.网络编程常见问题总结 串讲(七)

listen的时候的backlog有什么影响?

backlog代表连接的队列, 这里对于内核中其实会维护2个队列

未完成队列, 这个是服务器端接收到连接请求后会先放到这里(第一次握手)这个时候端口会处于SYN_RCVD状态

已完成队列,完成三次握手的连接会放到这里,这个时候才是连接建立

在我们 的linux环境中backlog 一般是被定义为已完成队列的长度,为完成队列一般是按照以完成队列长度的一半来取,backlog为5,那么已完成队列为5,未完成队列为3,总共是8个。如果这里的8个都被占满了,那么后面的连接就会失败,这里的行为可以由 /proc/sys/net/ipv4/tcp_abort_on_overflow 参数控制,这个参数打开后队列满了会发送RST包给client端,client端会看到Connection reset by peer的错误(线上部分内核打开了这个参数), 如果是关闭的话, 服务端会丢弃这次握手, 需要等待TCP的自动重连, 这个时间一般比较长, 默认情况下第一次需要3秒钟, 由于我们的连接超时一般都是很小的, client采用ullib库中的超时连接函数, 那么会发现这个时候连接超时了。

长连接和短连接混用是否会有问题?

虽然这种方式并不合适,但严格来说如果程序中做好相关的守护操作(包括一些情况下系统参数的调整)是不会出现问 题,基本来说在长短连接混用情况下出现的问题都是由于我们的程序存在不同程度上的缺陷造成的.

可能出现的问题:

只要有一端采用了短连接,那么就可以认为总体是短连接模式。

服务端长连接,客户端短连接

客户端主动关 闭,服务端需要接收到close的FIN包,read返回0 后才知道客户端已经被关闭。在这一段时间内其实服务端多维护了一个没有必要连接的状态。在同步模式(pendingpool,ub-xpool, ub-cpool, ub-epool)中由于read是在工作线程中,这个连接相当于线程多做了一次处理,浪费了系统资源。如果是IO异步模式(ub/apool或者使用 ependingpool读回调)则可以马上发现,不需要再让工作线程进行处理

服务端如果采用普通线程模型(ub-xpool)那么在异常情况下FIN包如果没有及时到达,在这一小段时间内这个处理线程不能处理业务逻辑。如果出现问题的地方比较多这个时候可能会有连锁反应短时间内不能相应。

服务端为长连接,对于服务提 供者来说可能早期测试也是采用长连接来进行测试,这个时候accept的baklog可能设置的很小,也不会出现问题。但是一旦被大量短连接服务访问就可能出现问题。所以建议listen的时候baklog都设置为128, 我们现在的系统支持这么大的baklog没有什么问题。

每次总是客户端主动断开,这导致客户端出现了TIME_WIAT的状态,在没有设置SO_LINGER或者改变系统参数的情况下,比较容易出现客户端端口不够用的情况。

服务端短连接,客户端长连接这个时候的问 题相对比较少,但是如果客户端在发送数据前(或者收完数据后)没有对脏数据进行检查,在写的时候都会出现大量写错误或者读错误,做一次无用的操作,浪费系统资源 一般的建议是采用长连接还是短连接,两端保持一致,但采用配置的方式并不合适,这个需要在上线的时候检查这些问题。比较好的方式是把采用长连接还是短连接放到数据包头部中。客户端发送的时候标记自己是采用 短连接还是长连接,服务端接收到后按照客户端的情况采取相应的措施,并且告知客户端。特别的如果服务端不支持长连接,也可以告知客户端,服务采用了短连 接

要注意的是,如果采用了一些框架或者库,在read到0的情况下可能会多打日志,这个对性能的影响可能会比较大。

网络编程常见问题总结 串讲(八)

select, epoll使用上的注意

select, epoll实现上的区别可以参考, 本质上来说 select, poll的实现是一样的,epoll由于内部采用了树的结构来维护句柄数,并且使用了通知机制,省去了轮询的过程,在对于需要大量连接的情况下在CPU上会有一定的优势.

select默认情况下可以支持句柄数 是1024, 这个可以看/usr/include/bits/typesizes.h 中的__FD_SETSIZE,在我们的编译机(不是开发机,是SCMPF平台的机器)这个值已经被修改为51200, 如果select在处理fd超过1024的情况下出现问题可用检查一下编译程序的机器上__FD_SETSIZE是否正确.

epoll在句柄数的限制没有像select那样需要通过改变系统环境中的宏来实现对更多句柄的支持

另外我们发现有些程序在使用epoll的时候打开了边缘触发模式(EPOLLET), 采用边缘触发其实是存在风险的,在代码中需要很小心,避免由于连接两次数据到达,而被只读出一部分的数据.EPOLLET的本意是在数据情况发生变化的时候激活(比如不可读进入可读状态), 但问题是这个时候如果在一次处理完毕后不能保证fd已经进入了不可读状态(一般来说是读到EAGIN的情况), 后续可能就一直不会被激活.一般情况下建议使用EPOLLET模式.一个最典型的问题就是监听的句柄被设置为EPOLLET, 当同时多个连接建立的时候, 我们只accept出一个连接进行处理, 这样就可能导致后来的连接不能被及时处理,要等到下一次连接才会被激活.小提示: ullib 中常用的ul_sreado_ms_ex,ul_swriteo_ms_ex内部是采用select的机制,即使是在scmpf平台上编译出来也还是受到 51200的限制,可用ul_sreado_ms_ex2,和ul_swriteo_ms_ex2这个两个接口来规避这个问题,他们内部不是采用 select的方式来实现超时控制的(需要ullib 3.1.22以后版本)

一个进程的socket句柄数只能是1024吗?

答案是否定的,一台机器上可以使用的socket句柄数是由系统参数 /proc/sys/fs/file-max 来决定的.这里的1024只不 过是系统对于一个进程socket的限制,我们完全可以采用ulimit的参数把这个值增大,不过增大需要采用root权限,这个不是每个工程师都可以采 用的.所以 在公司内采用了一个limit的程序,我们的所有的机器上都有预装这个程序,这个程序已经通过了提权可以以root的身份设置ulimit的 结果.使用的时候 limit./myprogram 进行启动即可,默认是可以支持51200个句柄,采用limit-n num 可以设置实际的句柄数.如果还需要更多的连接就需要用ulimit进行专门的操作.另外就是对于内核中还有一个宏NR_OPEN会限制fd的做大个数,目前这个值是1024*1024

小提示: linux系统中socket句柄和文件句柄是不区分的,如果文件句柄+socket句柄的个数超过1024同样也会出问题,这个时候也需要limit提高句柄数.

ulimit对于非root权限的帐户而言只能往小的值去设置, 在终端上的设置的结果一般是针对本次shell的, 要还原退出终端重新进入就可以了。

用limit方式启动,程序读写的时候出core?

这个又是另外一个问题,前面已经提到了在网络程序中对于超时的控制是往往会采用select或者poll的方式.select的时候对于支持的FD其 实是有上限的,可以看/usr/inclue/sys/select.h中对于fd_set的声明,其实一个__FD_SETSIZE /(8*sizeof(long))的long数组,在默认情况下__FD_SETSIZE的定义是1024,这个可以看 /usr/include/bits/typesizes.h 中的声明,如果这个时候这个宏还是1024,那么对于采用select方式实现的读写超时控制程序在处理超过1024个句柄的时候就会导致内存越界出 core .我们的程序如果是线下编译,由于许多开发机和测试这个参数都没有修改,这个时候就会造成出core,其实不一定出core甚至有些情况下会出现有数据但 还是超时的情况. 但对于我们的SCMPF平台上编译出来的程序是正常的,SCMPF平台上这个参数已经进行了修改,所以有时会出现QA测试没问题,RD 自测有问题的情况。

一台机器最多可以建立多少连接?

理论上来说这个是可以非常多的,取决于可以使用多少的内存.我们的系统一般采用一个四元组来表示一个唯一的连接{客户端ip, 客户端端口,服务端ip, 服务端端口}(有些地方算上TCP, UDP表示成5元组), 在网络连接中对于服务端采用的一般是bind一个固定的端口,然后监听这个端口,在有连接建立的时候进行accept操作,这个时候所有建立的连接都只 用到服务端的一个端口.对于一个唯一的连接在服务端ip和 服务端端口都确定的情况下,同一个ip上的客户端如果要建立一个连接就需要分别采用不同的端,一台机器上的端口是有限,最多65535(一个 unsigned char)个,在系统文件/proc/sys/net/ipv4/ip_local_port_range 中我们一般可以看到32768 61000 的结果,这里表示这台机器可以使用的端口范围是32768到61000, 也就是说事实上对于客户端机器而言可以使用的连接数还不足3W个,当然我们可以调整这个数值把可用端口数增加到6W.但是这个时候对于服务端的程序完全不受这个限制因为它都是用一个端口,这个时候服务端受到是连接句柄数的限制,在上面对于句柄数的说明已经介绍过了,一个 进程可以建立的句柄数是由/proc/sys/fs/file-max决定上限和ulimit来控制的.所以这个时候服务端完全可以建立更多的连接,这个 时候的主要问题在于如何维护和管理这么多的连接,经典的一个连接对应一个线程的处理方式这个时候已经不适用了,需要考虑采用一些异步处理的方式来解决, 毕竟线程数的影响放在那边

小提示: 一般的服务模式都是服务端一个端口,客户端使用不同的端口进行连接,但是其实我们也是可以把这个过程倒过来,我们客户端只用一个端但是服务端确是不同的端 口,客户端做下面的修改原有的方式 socket分配句柄-> connect 分配的句柄 改为 socket分配句柄->对socket设置SO_REUSEADDR选项->像服务端一样bind某个端口->connect 就可以实现

不过这种应用相对比较少,对于像网络爬虫这种情况可能相对会比较适用,只不过6w连接已经够多了,继续增加的意义不一定那么大就是了.

对于一个不存在的ip建立连接是超时还是马上返回?

这个要根据情况来看,一般情况connect一个不存在的ip地址,发起连接的服务需要等待ack的返回,由于ip地址不存在,不会有返回,这个时候会一直等到超时才返回。如 果连接的是一个存在的ip,但是相应的端口没有服务,这个时候会马上得到返回,收到一个ECONNREFUSED(Connection refused)的结果。

但是在我们的网络会存在一些有限制的路由器,比如我们一些机器不允许访问外网,这个时候如果访问的ip是一个外网ip(无论是否存在),这个时候也会马上返回得到一个Network is unreachable的错误,不需要等待。

第二篇:《网络编程课程设计》实习总结

河北科技师范学院欧美学院

实习类型教学实习实习单位河北科技师范学院欧美学院实习起止时间年月 年日 指导教师刘正林所在院(系)信息技术系专业班级网专1101学生姓名崔珺珺学号

一、实习的基本概况

(一)理论指导

根据实训内容自行确定

(二)项目情况

项目名称:企业门户网站

开发环境:Windows XP

项目分工情况:

我的任务:制作企业门户网站框架

二、实习过程或步骤

新建一个记事本文件,开始填写代码,我所做的是一家广告公司的主页面,所以题目的代码是这么写的:XX广告公司欢迎您!一个公司的主页必须有其他的链接,因为成员比较少,所以我做的比较简单,我用了

这样,超链接的字体是黑色的,但鼠标指向它以后就是红色的。

从互联网下载一张名为1后缀为JPG的图片,“XX广告,行业的领头者”下一步编写代码,并且将该1.JPG放入主页所在的文件夹,代码部分如下:

但是由于知识浅薄,图片放上去不是非常协调。

对网页上半部分的框架表格进行处理,的对字体、字号进行设置,代码如下:

首页

公司概况

公司机构

职务设置

工作科研

活动动态

联系客服

图书资料

客户服务

了解了一些代码的含义,比如一个表格可以分成很多行,用表示,比如指的是每行又可以分成许多单元格。

为了使网页有简单的交互性在主页上设置了一个登录界面和按钮,代码如下

用户名:

密码:

我连接了一个新建名为1.HTML的网页,主要是自己弄不太懂有关数据库的知识,所以只能简单的编写,这样不论在用户名、密码输入什么,都能跳入第二个页面。

进行了左侧表格框架的设计编写,代码如下:

服务导航

管理层

财务室

设计部

后续处理

服务接待

后勤供给

客户服务

订单档案

人事公告 最后。进行了简单的后续处理修改。

三、实习感受

(一)成绩与收获

通过这次网页制作实习课程,我更加深入的了解了各种代码语言的作用,从互联网上学习了更多的知识。学习制作网页,必须先从学习HTML语言做起,弄清楚各种语言在制作网页中的重要作用,通过这次综合性实验,我已经可以灵活运用所学知识和技巧制作简单的网页。实验过程中,我尽量充分运用老师教过的知识,对所学知识有一定的巩固作用,为了做出更好的效果我也翻阅参考了其他的资料,学习到了更多网页处理的技巧,制作网页过程中我遇到了不少的问题,通过询问同学和查找资料都获得了 解决的办法,这次实践对我而言是一个不小的提升,在实践过程中学习巩固对知识能有更深的记忆。网页制作是一门很实用的学科,值得我们以后更深入的去学习。现在我掌握的制作知识还是太少,需要以后多与老师、同学们交流,自己在网络上也需要更加深入的挖掘有关知识。

(二)问题与不足

对一些代码的作用不了解,乱加运用导致页面混乱,而且缺少独自处理错误与难题的能力,对于有些知识仅是一看就过,其实自己根本没有掌握,而且掌握了的也是一知半解,没有将知识加以用工,总是想做些什么但是真到了做的时候不会,还得去翻书。

第三篇:网络编程实验报告

实验一 TCP Socket API程序设计

一、预备知识

1.网络编程基本概念

网络上的计算机间的通讯,实质上是网络中不同主机上的程序之间的通讯。在互联网中使用IP地址来标识不同的主机,在网络协议中使用端口号来标识主机上不同进程,即使用(IP地址,端口号)二元组。

套接字(Socket)用于描述IP地址和端口,是一个通信链的句柄,通信时一个网络程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过与网络接口卡相连的传输介质将这段信息发送到另一台主机的Socket中,以供其他程序使用。

图1-1 TCP通信流程 2.TCP通信流程

TCP程序是面向连接的,程序运行后,服务器一直处于监听状态,客户端与服务器通信之前必须首先发起连接请求,由服务器接收请求并在双方之间建立连接后才可以互相通信。

二、实验目的

1.了解Winsock API编程原理; 2.掌握TCP Socket程序的编写; 3.了解C/S模式的特点; 4.学会解决实验中遇到的问题。

三、实验任务

使用Winsock API相关类实现TCP Socket通信程序,并能成功运行。

四、实验环境及工具

1.Windows2000/XP/7 2.Visual C++开发平台 3.Visual Studio2010

五、实验内容和步骤

参照《Visual C++网络编程教程》书中81页,TCP Socket API程序设计。连接:

void CChatClientDlg::OnConnect(){

WSADATA wsd;

//WSADATA结构

WSAStartup(MAKEWORD(2,2),&wsd);

//加载协议,使用Winsock 2.2版

m_client = socket(AF_INET,SOCK_STREAM,0);//创建流式套接字

//服务器地址

sockaddr_in serveraddr;

UpdateData();

if(ServerIP.IsBlank())

{

AfxMessageBox(“请指定服务器IP!”);

return;

}

if(sPort.IsEmpty())

{

AfxMessageBox(“请指定端口!”);

return;

}

//获取服务器进程的IP和端口

BYTE nFild[4];

CString sIP;

ServerIP.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);

sIP.Format(“%d.%d.%d.%d”,nFild[0],nFild[1],nFild[2],nFild[3]);

//设置服务器地址结构的内容

serveraddr.sin_family = AF_INET;

serveraddr.sin_addr.S_un.S_addr = inet_addr(sIP);

serveraddr.sin_port = htons(atoi(sPort));

//发起连接须指明要访问的服务器进程地址,这个地址存储在serveraddr中

if(connect(m_client,(sockaddr*)&serveraddr,sizeof(serveraddr))!= 0)

{

MessageBox(“连接失败”);

return;

}

else

{

m_ListWords.AddString(“连接服务器成功!”);

m_ListWords.SetTopIndex(m_ListWords.GetCount()1);

ServerIP.EnableWindow();

ServerPort.EnableWindow();

m_ButtonConnect.EnableWindow();

m_ButtonDisconnect.EnableWindow(false);

m_EditWords.EnableWindow(false);

m_ButtonSend.EnableWindow(false);

m_ButtonExit.EnableWindow();}

“发送”按钮事件过程代码如下:

void CChatClientDlg::OnSend(){

//向服务器发送信息

UpdateData();

if(m_sWords.IsEmpty())

{

AfxMessageBox(“发送的消息不能为空!”);

return;

}

//开始发送数据

int i = send(m_client,m_sWords.GetBuffer(0),m_sWords.GetLength(),0);

m_ListWords.AddString(“发送:” + m_sWords);

m_ListWords.SetTopIndex(m_ListWords.GetCount()1);

closesocket(m_client);

ServerIP.EnableWindow();

ServerPort.EnableWindow();

m_ButtonConnect.EnableWindow();

m_ButtonDisconnect.EnableWindow(false);

m_EditWords.EnableWindow(false);

m_ButtonSend.EnableWindow(false);

m_ButtonExit.EnableWindow();} “清空”按钮的事件过程: m_ListWords.ResetContent();“关于”按钮的事件过程: CAboutDlg dlgAbout;dlgAbout.DoModal();

服务器端: 开始监听代码:

void CChatServerDlg::OnListen(){

WSADATA wsd;

//WSADATA结构

WSAStartup(MAKEWORD(2,2),&wsd);

//加载协议栈,使用Winsock 2.2版

m_server = socket(AF_INET,SOCK_STREAM,0);//创建流式套接字

//将网络中的事件关联到窗口的消息函数中,定义消息号为20000,侦测客户端的连接请求

WSAAsyncSelect(m_server,m_hWnd,20000,FD_ACCEPT);

m_client = 0;

BYTE nFild[4];

CString sIP;

UpdateData();

if(ServerIP.IsBlank())

{

AfxMessageBox(“请设置IP地址!”);

return;

}

if(sPort.IsEmpty())

{

AfxMessageBox(“请设置监听端口!”);

return;

}

ServerIP.GetAddress(nFild[0],nFild[1],nFild[2],nFild[3]);

sIP.Format(“%d.%d.%d.%d”,nFild[0],nFild[1],nFild[2],nFild[3]);

//服务器地址

sockaddr_in serveraddr;

serveraddr.sin_family = AF_INET;

serveraddr.sin_addr.S_un.S_addr = inet_addr(sIP);

serveraddr.sin_port = htons(atoi(sPort));

//绑定地址

if(bind(m_server,(sockaddr*)&serveraddr,sizeof(serveraddr)))

{

MessageBox(“绑定地址失败.”);

return;

}

//监听开始,服务器等待连接请求的到来

listen(m_server,5);

m_ListWords.AddString(“监听开始:”);

m_ListWords.AddString(“地址” + sIP + “ 端口” + sPort);

m_ListWords.AddString(“等待客户端连接„„”);

//界面完善

m_ListWords.SetTopIndex(m_ListWords.GetCount()-1);

ServerIP.EnableWindow(false);

ServerPort.EnableWindow(false);

m_ButtonListen.EnableWindow(false);

m_ButtonStopListen.EnableWindow();

m_ButtonClear.EnableWindow();

m_ButtonExit.EnableWindow(false);} “停止监听”按钮事件过程代码如下: void CChatServerDlg::OnStopListen(){

//停止监听

closesocket(m_server);

m_ListWords.AddString(“停止监听”);

m_ListWords.SetTopIndex(m_ListWords.GetCount()1);} “断开”按钮事件过程代码如下: void CChatServerDlg::OnDisconnect(){

closesocket(m_client);

m_ListWords.AddString(“与客户端断开”);

m_ListWords.SetTopIndex(m_ListWords.GetCount()1);

//界面完善

m_ButtonDisconnect.EnableWindow();

m_EditWords.EnableWindow();

m_ButtonSend.EnableWindow();} ReceiveData()函数代码如下:

void CChatServerDlg::ReceiveData(){

//接收客户端的数据

char buffer[1024];

int num = recv(m_client,buffer,1024,0);

buffer[num] = 0;

CString sTemp;

sTemp.Format(“收到:%s”,buffer);

m_ListWords.AddString(sTemp);//显示信息

m_ListWords.SetTopIndex(m_ListWords.GetCount()1);

closesocket(m_client);//关闭与客户端通信的Socket

WSAAsyncSelect(m_server,m_hWnd,20000,FD_ACCEPT);//准备接收新的客户端连接

//界面完善

m_ButtonDisconnect.EnableWindow(false);

m_EditWords.EnableWindow(false);

m_ButtonSend.EnableWindow(false);} 服务器的初始化代码如下: //界面初始化

m_ButtonStopListen.EnableWindow(false);m_ButtonDisconnect.EnableWindow(false);m_ButtonClear.EnableWindow(false);m_EditWords.EnableWindow(false);m_ButtonSend.EnableWindow(false);

运行结果:

六、思考题

1.用Winsock API编程时,主要进行哪些通行的操作步骤? 2.阐述C/S模式的通信过程。答:

1.通行的操作

1.Winsock的打开(WSAStartup())。2.建立套接字(socket()或WSASocket())。3.地址绑定(bind())。

4.服务器监听连接(listen())。

5.客户端提出连接申请(connect()或WSAConnect())。6.服务器接收客户端的连接请求(accept()或WSAAccept())。7.数据的发送(send()或WSASend(),sendto()或WSASendTo())。8.数据的接收(recv()或WSARecv(),recvfrom()或WSARecvfrom())。9.关闭套接字(closesocket())。10.关闭Winsock(WSACleanup())。

2通信过程

第四篇:网络编程实习作业

网络编程实习作业

一. 实习内容:

 了解插口(socket)实现原理。

 在某一种平台(Linux,Unix系列或Windows系列)下进行网络客户机-服务器编程。

二. 主要参考书目:

 W.Richard Stevens, “UNIX Network Programming Networking APIs: Scokets and XTI(Volume 1,Second Edition)”,清华大学出版社 Prentice-Hall International, Inc.或其中文版 “UNIX网络编程(第1卷,第2版)” 经典读物,目前的最新版是第三版

 Douglas Comer,《用TCP/IP进行网际互连 第3卷:客户机-服务器编程和应用(第2版)》 或其英文版 Internetworking With TCP/IP Vol III: Client-Server Programming And Application(Second Edition)以下简称《客户机-服务器编程》

三. 实习题目:

1:编写DAYTIME服务的UDP客户机和服务器的实现。

基本要求:

当服务器端收到客户端的请求后,将当前daytime返回给客户端,客户端收到该回应后,将收到的daytime显示到输出中。

(其中在Linux或Unix环境中,当前daytime的获得参考函数time()和ctime())

2:熟悉Http协议的请求和响应格式,编写一个简单的Http服务器。题目描述参考《自上而下计算机网络》(第三版 作者James F.Kurose)第二章课后编程作业1。

基本要求:正确解析Http请求,实现简单的GET请求回应。模拟一个对象(如:文件index.html)的GET回应(如:回应一个字符串),对于其他的对象,则根据Http响应格式回应对象不存在信息。3 通过浏览器可检测自己的程序。如:输入,查看其响应结果。对http请求的处理必须采用多进程实现,即主进程负责等待请求连接,每当收到一个请求后,产生一个子进程对该请求做单独处理,主进程继续等待新请求,子进程在处理完其请求后结束自己。详细要求参考课本要求。(多进程编程参考函数fork())

要求:

源程序部分带有必要的注释,备有一份文档说明各个程序中的思路和关键技术。注意,只能使用C语言编写。可以是windows或者linux下可执行,二种环境选择一种即可。

评分标准

90% 以上 :在程序说明文档中,可以体现出自己对本程序所用到的技术有较深刻的理解。

程序有较好的可读性(关键部分的注释比较详细)。

80% :符合要求,程序说明的比较详细,思路比较清楚。

70% :只有程序没有说明文档的,70%-60% :缺少说明文档

60% 以下 :程序或说明文档完全和别人的一样(抄袭于被抄袭者一样处理)。

注:作业包括程序部分(.c,.cpp,.h等源码和.exe等可执行文件,不要.obj 等其他文件)和程序说明文档,文件大小最好不要超过1M。程序说明要体现出你所用到的关键技术,要说清楚自己定义函数的功能及实现,要有关键部分的流程图。要按时交作业,晚交的要相应的扣分。请将程序打成一个包,包名统一采用“学号_姓名”形式,然后发到

network_tju@163.com

邮件的Title也是“学号_姓名”

请一定将学号放在前面

5截至日期:2009年4月20日

(注:1.以邮箱接收到的时间戳为准。2.在提交作业时,请最好选择发送并保存邮件,避免由于投递失败而导致作业无法按时完成。(就是避免死无对证)3.杜绝抄袭,请尽早开始着手这项作业)

第五篇:网络编程实习报告

[实习目的]

通过理论联系实际,巩固所学的知识,提高处理实际问题的能力,并为自己能顺利与社会环境接轨做准备。[实习任务]Linux下网络服务器开发(基于C语言);本文总结了我对Linux下网络服务器模型的认识。[实习内容]一.循环服务器1.循环服务器在同一个时刻只可以响应一个客户端的请求,对多个客户程序的处理是采用循环的方式进行; 2.UDp循环服务器的实现非常简单:UDp服务器每次从套接字上读取一个客户端的请求,处理, 然后将结果返回给客户机;2.1.算法如下(UDp服务器): socket(...);

bind(...);

while(1)

{

recvfrom(...);

process(...);

sendto(...);

}3.TCp循环服务器的实现也不难:TCp服务器接受一个客户端的连接,然后处理,完成了这个客户的所有请求后,断开连接;3.1.算法如下(TCp服务器):

socket(...);

bind(...);

listen(...);

while(1)

{

accept(...);

while(1)

{

read(...);

process(...);write(...);

}

close(...);

}3.2.TCp循环服务器一次只能处理一个客户端的请求.只有在这个客户的所有请求都满足后, 服务器才可以继续后面的请求.这样如果有一个客户端占住服务器不放时,其它的客户机都不能工作了.因此,TCp服务器一般很少用循环服务器模型的.二.并发服务器1.为了弥补循环TCp服务器的缺陷,人们又想出了并发服务器的模型。并发服务器的思想是每一个客户机的请求并不由服务器直接处理,而是服务器创建一个子进程来处理;2.使用并发服务器可以使服务器进程在同一个时刻有多个子进程和不同的客户程序连接、通信;在客户程序看来,服务器可以同时并发地处理多个客户的请求;3.算法如下(TCp服务器):socket(...);

bind(...);

listen(...);

while(1)

{

accept(...);

if(fork(..)==0)

{

close(...);while(1)

{

read(...);

process(...);

write(...);

}

close(...);

exit(...);

}

close(...);

}4.TCp并发服务器可以解决TCp循环服务器客户机独占服务器的情况,改善了对客户程序的响应速度;不过也同时带来了一个不小的问题:为了响应客户机的请求,服务器要创建子进程来处理,而创建子进程是一种非常消耗资源的操作,这明显增加了系统调度的开销;5.为了解决创建子进程带来的系统资源消耗,人们又想出了多路复用I/O模型.5.1.该模型一般用函数select和相关的四个宏定义:intselect(intfd,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout)

voidFD_SET(intfd,fd_set*fdset)

voidFD_CLR(intfd,fd_set*fdset)

voidFD_ZERO(fd_set*fdset)

intFD_ISSET(intfd,fd_set*fdset)5.2.一般的来说当我们在向文件读写时,进程有可能在读写时候阻塞,直到一定的条件满足.比如我们从一个套接字读数据时,可能缓冲区里面没有数据可读(通信的对方还没有发送数据过来),这个时候我们的读调用就会等待(阻塞)直到有数据可读.如果我们不希望阻塞,我们的一个选择是把socket设置为非阻塞模式来实现;int socketfd;socketfd=socket(AF_INET,SOCK_STREAM,0);fcntl(socketfd,F_SETFL,O_NONBLOCK);通过设置socket为非阻塞模式,可以实现“轮循”多个socket,当企图从一个没有数据等待处理的非阻塞socket读取数据时,函数立即返回,但是这种“轮循”会使CpU处于忙等待方式,降低了性能,select函数解决了这个问题;5.3.在我们调用select时进程会一直阻塞直到以下的一种情况发生.1)有文件可以读.2)有文件可以写.3)超时所设置的时间到;5.4.算法如下(多路复用I/O模型):初始化(socket,bind,listen);

while(1)

{ 设置监听读写文件描述符(FD_*);

调用select;

如果是倾听套接字就绪,说明一个新的连接请求建立

建立连接(accept);

加入到监听文件描述符中去;

否则说明是一个已经连接过的描述符

进行操作(read或者write);}

多路复用I/O可以解决资源限制的问题.着模型实际上是将UDp循环模型用在了TCp上面.这也就带来了一些问题.如由于服务器依次处理客户的请求,所以可能会导致有的客户会等待很久。三.I/O模型1.网络服务器模型根据I/O模型的不同实现而来的;2.I/O模型分为同步I/O和异步I/O;同步I/O又包括阻塞I/O、非阻塞I/O、信号驱动I/O、多路复用I/O;可根据不同的要求利用不同的I/O模型实现不同是网络服务器。[实习心得] 通过近几个月的实习,基本上掌握了Linux下C语言网络编程的一些算法和技巧,提高了自己的能力。专业:计算机网络技术 班级:03631 学号:63103089 姓名:吕亮亮——XX.05.23

下载网络编程常见问题总结word格式文档
下载网络编程常见问题总结.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐

    网络编程实习报告

    广东应届生实习报告网在线编辑整理本文。[实习目的] 通过理论联系实际,巩固所学的知识,提高处理实际问题的能力,并为自己能顺利与社会环境接轨做准备。[实习任务]Linux下网络服......

    java网络编程考试总结要点(推荐五篇)

    1、动静态网页的概念,常见后缀及基本区别。 静态网页:是指用HTML标记语言等来编排,页面中的内容固定不变,存盘后一般以*.html、*.htm等文件形式存在的网页。 动态网页:是指网页中......

    嵌入式串口和网络编程实验报告

    嵌入式实验报告 题目:linux串口和网络编程 一、实验目的: 1、强化本学期所学的相关的内容。 2、掌握串口相关设置。 3、强化基于TCP网络传输的三次握手。 4、自学Linux......

    《网络高级编程》实验报告要求

    一、 实验报告为纸质报告,手写,不少于8页纸。 二、 报告首页为标准实验报告封面,有课程名称、实验名称、 姓名、时间 三、 报告内容为: 1、 2、 3、 4、 实验目的 实验题目 实验......

    网络发票常见问题解决方法

    网络发票常见问题解决方法如下: 1. 电子发票的登录密码忘记了怎么办? 财务负责人和代理开票人的密码忘记了,可以通过system用户进行修改,system用户和纳税申报登录密码忘记了,需......

    刀具和编程总结

    ① 白钢刀(即高速钢刀具)因其通体银白色而得名,主要用于直壁加工。白钢刀价格便宜,但切削寿命短、吃刀量小、进给速度低、加工效率低,在数控加工中较少使用。 ② 飞刀(即镶嵌式刀......

    编程题总结(范文大全)

    C作业汇总 1. short a,b=32767; /*short类型在内存中占2B*/ a=b+1; 问:a的值是多少?并分析原因。 2. 有一4位数整数,假设用abcd表示,请把这个4位数的每个数位用表达式表示出来......

    数据库编程总结(推荐)

    数据库编程总结 当前各种主流数据库有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS A......