IPython下的网络协议学习-------以echo回显程序为例
来源:互联网 发布:东方不败网络歌手 编辑:程序博客网 时间:2024/05/29 17:18
在进入正文之前强烈推荐IPython 来学习网络编程,和C语言繁琐的语法和编译相比,IPython交互的输入输出效率奇高,打开两个IPython页面就可以方便的编写服务端和客户端程序了。推荐anaconda环境,集成了很多必备的库,以及IPython notebook 。
废话说完了,下面进入正题。本文主要以简单的回显程序为例,实践测试了不同异常情况下服务端和客户端的反应,由此逐步提高程序的健壮性。异常情况主要参考网络编程著作《UNIX网络编程》第三版。
我的服务器代码和客户端代码是这样的:
#Echo Serverimport socketimport sysimport signalimport osdef server_echo(connefd): while 1: try : print ("before recv") mesg = connefd.recv(8) print (mesg) if not mesg: return connefd.send(mesg) except os.error: print ("socket error") return def signal_handlers(signo,frame): #信号处理函数 print ("child %d terminnated",os.wait())serverPort = 50000listenfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)listenfd.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)listenfd.bind(("",serverPort))listenfd.listen(5)signal.signal(signal.SIGCHLD,signal_handlers) while 1: connefd,address = listenfd.accept() childpid = os.fork() if(childpid == 0): listenfd.close() server_echo(connefd) exit(0) connefd.close()
#客户端import socketconnefd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)connefd.connect(('127.0.0.1',5000))while 1: sentence = input("Enter your sentence:") if sentence == "": connefd.close() break connefd.send(sentence.encode()) mesg = connefd.recv(8) print mesg
代码很简单,比较值得一提的是服务器程序中利用系统调用fork函数进行实现并发服务器。除此之外,必须提供一个SIGCHLD信号的处理程序。当子进程结束时,内核会自动发送SIGCHLD信号给父进程,默认处理方式是忽略。那么我们为什么要处理SIGCHLD信号呢?原因是如果不主动处理SIGCHLD会产生大量僵尸进程。
一个进程在调用exit命令结束自己的生命的时候,其实它并没有真正的被销毁,而是留下一个称为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁)
因此,仅仅调用exit()不能完全的释放进程资源,我们必须**捕捉(capture)**SIGCHLD信号,在父进程的信号处理程序中释放资源。为此,Unix系统提供了wait,waitpid等函数获取子进程的终止状态,并释放其资源。那么wait和waitpid这些函数有什么作用呢?顾名思义,wait函数是一个等待的函数,父进程调用wait函数来等待一个子进程终止,并清理第一个终止子进程的“尸体”。那么waitpid函数的作用又是什么呢?waitpid函数相当于一个加强版的wait函数,他的函数参数中可以设定需要等待的子进程ID,如果没有子进程已终止则可选择不阻塞等选项。参阅UNPV1第三版可以了解waitpid更详细的信息。
接下来就UNP 5.11节 至 5.16节的小节所述的异常情况进行模拟,并总结一些异常处理方法和编程技巧。
(1) 服务器程序在accept返回前连接终止
accept函数是服务器程序调用的一个函数,作用是服务器接受一个ESTABLISHED状态的TCP连接,并生成一个新的套接字,叫做已连接套接字(connected socket) 。函数接受的套接字是由listening socket 维护的 已连接队列中的队首的连接,如果队列为空,则accept函数阻塞。
注意:TCP连接在客户端调用connect函数返回时就已经完成连接,这意味着服务端在调用 accept函数之前TCP连接已经建立。
为了模拟accept函数在返回前连接终止,我们可以让客户端在connect函数返回后,利用SO_LINGER套接字选项来改变close函数的行为(让其向服务端发送RST报文段)。在RST报文段发送之前,我们的服务端accept函数应该返回。因此可以在服务端调用accept函数前利用input函数或是sleep函数阻塞程序,当RST报文端发送后再调用accept函数。服务端和客户端代码改变如下:
#服务端while 1: input("Enter Any key to continue") #添加的阻塞语句 connefd,address = listenfd.accept()
#客户端:connefd.connect(('127.0.0.1',5000))connefd.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,struct.pack("ii",True,0))#添加的套接字选项,改变了套接字close函数的执行内容,关于SO_LINGER套接字选项可以查阅UNP7.5节connefd.close()
让我们运行代码测试一下结果,同时可以利用tcpdump或者是Wireshark抓包工具监测RST报文段是否发送成功。
客户端代码执行时的Wireshark抓包截图
最后让我们来看一下服务端收到RST报文段后服务端的响应。
ConnectionResetError Traceback (most recent call last)<ipython-input-1-4ac06058bc37> in <module>() 32 if(childpid == 0): 33 listenfd.close()---> 34 server_echo(connefd) 35 os._exit(0) 36 connefd.close()<ipython-input-1-4ac06058bc37> in server_echo(connefd) 8 # try : 9 print ("before recv")---> 10 mesg = connefd.recv(8) 11 print (mesg) 12 if not mesg:ConnectionResetError: [Errno 104] Connection reset by peer
可以看到服务端返回一个ConnectionResetError错误,而且继续调用accept函数,之后其他客户端也能继续连接服务端正常运行程序。我们也能捕捉ConnectionResetError,进而实现自定义的错误处理方法。
后续将继续对不同异常情况进行试验,记录学习TCP/IP协议的过程与经验。
W.Richard Stevens.UNIX网络编程 卷1:套接字联网API[M].北京:人民邮电出版社,2010
- IPython下的网络协议学习-------以echo回显程序为例
- 网络流媒体下载的 10 种方法(以下载 Echo 音乐为例)
- 基于UDP协议的网络程序 (学习)
- 程序猿的快速学习法——以JS学习为例,进行图解
- WinSock网络编程学习(一)Echo客户/服务器程序
- 【Android学习】网络通信----以HttpURLConnection Post请求为例
- Linux下MOOS编程(以应急程序为例)
- PPP协议的配置(以H3C模拟器为例)
- 【caffe-Windows】caffe在Windows下训练深度学习网络并测试(以mnist为例)
- NSIS学习笔记(以Qt4程序打包为例)
- NSIS学习笔记(以Qt4程序打包为例)
- NSIS学习笔记(以Qt4程序打包为例)
- NSIS学习笔记(以Qt4程序打包为例)
- 学习的全球化,以Unix为例
- 认证在网络协议中的应用(以OSPF为例)
- ECHO网络程序的演变史 (1) --- Socket地址
- Linux下USB 输入子系统的学习(以鼠标驱动为例)
- 【Java学习】遍历文件夹下所有的文件【以txt为例】
- PHP5中使用PDO连接数据库的方法
- java多线程学习记录
- 数据结构之二叉树的前序遍历、中序遍历、后序遍历、层序遍历
- php连接mssql数据库的几种方式
- python set
- IPython下的网络协议学习-------以echo回显程序为例
- ORACLE逻辑操作符
- PHP PDO函数库详解
- java参数传递的最终理解
- jQuery和AngularJS的区别
- 集群的设计思路和常用的技术
- Hydra工具解析
- Android Studio 下获取开发版应用(application)的SHA1---方法之一
- 浅谈C++多态性