网络编程
一、客户端与服务器架构
硬件的客户端与服务器架构:打印机服务器,文件服务器
硬件服务器有关门的设备,打印机服务器有打印机,文件服务器NFS有磁盘。
软件的客户端与服务器架构:web服务器,数据库服务器,窗口服务器
软件服务器主要提供程序的运行,数据的发送与接收,合并,升级或其他的程序或数据的操作。
在完成服务之前,服务器必须要先完成一些设置。先要创建一个通讯端点,然服务器能“监听”请求。一单通讯端点创建好之后,我们在“监听”的服务器就可以进入他那等待和处理客户请求的无限循环中了。服务器准备好后,也要用之潜在的客户,让他们指导服务器已近准备好处理服务了,否则没有人会提请求的。
二、套接字:通信端点
套接字是一种具有之前所说的“通信端点”概念的计算机网络数据结构。套接字有两种,分别是基于文件型的和基于网络型的。
基于文件型的套接字家族有:AF_UNIX,基于网络的套接字家族有:AF_NETLINK和AF_INET。
三、python中的网络编程
1、socket()模块函数
要使用socket.socket()函数来创建套接字。其语法如下:
socket(socket_family,socket_type,protocol=0)
如前所述,socket _family 不是AF_UNIX就是AF_INET;socket_type可以是SOCK_STREAM或SOCK_DGREAM;protocol一般不填,默认值为0.
创建一个TCP/IP的套接字,你可以这样调用socket.socket():
tcpSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
同样的,创建一个UDP/IP的套接字,你要这样:
udpSocket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
由于socket模块中有太多的属性,我们在这里破例使用了’from module import *’语句。使用这个语句,我们就把socket模块里的所有属性都带到我们的命名空间里了,这样就能大幅度减短我们的代码。
tcpSocket = socket(AF_INET,SOCK_STREAM)
当我们创建了套接字对象后,所有的交互都将通过对该套接字对象的方法调用来进行。
3、创建一个TCP服务器
我们这里只是一个模型。
ss = socket() #创建服务器套接字
ss.bind() #把地址绑定到套接字上
ss.listen() #监听连接
inf_loop: #服务器无限循环
cs = ss.accept() #接收客户端连接
comm_loop: #通信循环
cs.recv()/cs.send() #对话(接收与发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器套接字
所有的套接字都用socket.socket()函数来创建。服务器需要“坐在某个端口上“等待请求。所以他们必须要”绑定“到一个本地的地址上。由于TCP是一个面向连接点的通信系统,在TCP服务器开始工作之前,要先完成一些设置。TCP服务器必须”监听“(进来的)的链接,设置完成之后,服务器就可以进入无限循环了。
一个简单的(单线程的)服务器会调用accept()函数等待链接的到来。默认情况下,accept()函数是阻塞式的,及程序在连接到来之前处于挂起状态。套接字也支持非阻塞模式。
一旦接收一个链接,accept()函数就会返回一个单独的客户端套接字用于后续的通信。使用新的客户端套接字就想把客户的电话转接给一个客户服务人员。当一个客户打电话进来的时候,总计接了电话,然后把电话转接到合适的人那里处理客户的需求。
这样就可以空出总机,也就是最初的那个服务器套接字,于是,话务员就可以等待下一个电话(客户端的请求),与此同时,前一个客户与对应的客户服务人员在另一条线路上进行着他们之间的对话。同样的每当一个请求到来时,要创建一个新的端口,然后直接在哪个端口上与客户对话,这样就可以空出主端口来接受其他客户的链接。
在临时套接字创建好之后,通信就可以开始了。客户与服务器要使用这个新创建的套接字进行数据的发送与接收,直到通讯的某一方关闭了连接或发送了一个空字符串之后,通讯就结束了。
TCP时间戳服务器
[root@localhost opt]# cat -n tsTserv.py
1 #!/usr/bin/python
2
3 from socket import *
4 from time import ctime
5
6 HOST = ''
7 PORT = 21567
8 BUFSIZ = 1024
9 ADDR = (HOST,PORT)
10
11 tcpSerSock = socket(AF_INET,SOCK_STREAM)
12 tcpSerSock.bind(ADDR)
13 tcpSerSock.listen(5)
14
15 while True:
16 print 'waiting for onnection..'
17 tcpCliSock,addr = tcpSerSock.accept()
18 print '...connected from:',addr
19
20 while True:
21 data = tcpCliSock.recv(BUFSIZ)
22 if not data:
23 break
24 tcpCliSock.send('[%s] %s' %(ctime(),data))
25
26
27 tcpSerSock.close()
28
逐行解释
1~4行
第一行是Unix的启动信息行,随后我们导入了time.ctime()函数和socket模块的所有属性。
6~13行
HOST变量为空,表示bind()函数可以绑定在所有有效的地址上。我们还选用了一个随机生成的未被占用的端口号。在程序中,我们把缓冲区的大小设定为1K。你可以根据网络情况和应用的需要来修改这个大小。listen()函数的参数只是表示最多允许多少个连接同时连进来,而后来的连接就会被拒绝掉。
TCP服务器的套接字(tcpSerSock)在第11行被生成。随后把套接字绑定到服务器的地址上,然后开始TCP监听。
15~28行
在进入到服务器的无限循环后,我们被动地等待连接的到来。当有连接时,我们进入对话循环,等待客户端发送数据。如果消息为空,表示客户端已经推出,那就再去等待下一个客户端连接。得到客户端消息后,我们在消息前加一个时间戳后返回。最后一行不会被执行到,放在这里用于提醒我们,在服务器退出的时候,要记得调用close()函数。
4、创建TCP客户端
我们也创建一个代码框架
cs = socket() # 创建客户端套接字
cs.connect() #尝试连接服务器
comm_loop: #通信循环
cs.send()/cs.recv() #对话(发送、接收)
cs.close() #关闭客户端套接字
如前所述,所有的套接字都由socket.socket()函数创建。在客户端有了套接字之后,马上就可以调用connet()函数去连接服务器。连接建立后,就可以与服务器开始对话了。在对话结束之后,客户端就可以关闭套接字
[root@localhost opt]# cat -n tsTchlnt.py
1 #!/usr/bin/python
2 from socket import *
3
4 HOST = 'localhost'
5 PORT = 21567
6 BUFSIZE = 1024
7 ADDR = (HOST,PORT)
8
9 tcpCliSock = socket(AF_INET,SOCK_STREAM)
10 tcpCliSock.connect(ADDR)
11
12 while True:
13 data = raw_input('>')
14 if not data:
15 break
16 tcpCliSock.send(data)
17 data = tcpCliSock.recv(BUFSIZ)
18 if not data:
19 break
20 print data
21
22 tcpCliSock.close()
5~11行
HOST和PORT变量表示服务器的主机名由于我们在同一台电脑上进行测试,所以HOST里放的是本机的主机名(如果你的服务器运行在其它电脑上,要做相应的修改)。端口号与服务器上设置的端口号完全相同。缓冲区大小还是设为1K。
TCP客户套接字(tcpCliSock)在第10行创建。然后就去连接服务器。
13~23行
客户端也有个无限循环,但这跟服务器的那个不期望推出的无限循环不一样。客户端的循环在以下两个条件的任意一个发生后就退出:用户没有输入任何内容(14~16行)或 服务器由于某种原因退出,导致recv()函数失败(18~20行)。否则,在一般情况下,客户端会把用户输入的字符串发给服务器进行处理,然后接收并显示服务器传来的加了时间戳的字符串。
5、运行我们的客户端与TCP服务器
先执行服务端的脚本,先启动服务器
[root@localhost opt]# ./tsTserv.py
waiting for onnection..
...connected from: ('127.0.0.1', 58318)
再执行客户端的脚本,与服务器建立连接
[root@localhost opt]# ./tsTchlnt.py
>wulil
[Mon Feb 22 16:02:49 2016] wulil
>wowo
[Mon Feb 22 16:02:50 2016] wowo
>li
[Mon Feb 22 16:02:53 2016] li
>
6、创建一个UDP服务器
由于UDP服务器不是面向连接的,所以不用像TCP服务器那样做那么多设置工作。事实上,并不用设置什么,直接等待近来的链接就好了。
ss = socket() #创建服务器套接字
ss.bind() #绑定服务器套接字
inf_loop: #服务器无限循环
cs = ss.recvfrom()/ss.sendto()#对话(接收与发送)
ss.close() #关闭服务器套接字
从伪代码中可以看出,使用的还是那套先创建套接字然后绑定到本地地址(主机/端口)的方法。无限循环中包含了从客户端接收消息,返回加了时间戳的结果和回去等写一个消息这三步。同样的,由于代码不会跳出无限循环,所以,close()函数调用是可选的。我们这一句话的原因在于提醒开发人员,在设计的时候能够设计一个更智能的退出方案,要确保close()函数会被调用。
服务器端
[root@localhost opt]# cat -n tsUserv.py
1 #!/usr/bin/python
2 from socket import *
3 from time import ctime
4
5 HOST = ''
6 PORT = 21567
7 BUFSIZ = 1024
8 ADDR = (HOST,PORT)
9
10 udpSerSock = socket(AF_INET,SOCK_DGRAM)
11 udpSerSock.bind(ADDR)
12
13 while True:
14 print 'waiting for meassage..'
15 data,addr = udpSerSock.recvfrom(BUFSIZ)
16 udpSerSock.sendto('[%s] %s' %(ctime(),data),addr)
17 print '...received from and returned to :',addr
18
19 udpSerSock.close()
客户端
[root@localhost opt]# cat -n tsUclnt.py
1 #!/usr/bin/python
2 from socket import *
3
4 HOST = 'localhost'
5
6 PORT = 21567
7 BUFSIZ = 1024
8 ADDR = (HOST,PORT)
9
10
11 udpCliSock = socket(AF_INET,SOCK_DGRAM)
12
13 while True:
14 data = raw_input('>')
15 if not data:
16 break
17 udpCliSock.sendto(data,ADDR)
18 data,ADDR = udpCliSock.recvfrom(BUFSIZ)
19 if not data:
20 break
21 print data
22
23 udpCliSock.close()
[root@localhost opt]# ./tsUserv.py
waiting for meassage..
...received from and returned to : ('127.0.0.1', 43523)
waiting for meassage..
...received from and returned to : ('127.0.0.1', 45046)
waiting for meassage..
...received from and returned to : ('127.0.0.1', 45046)
waiting for meassage..
[root@localhost opt]# ./tsUclnt.py
>iiii
[Tue Feb 23 14:08:30 2016] iiii
>lll
[Tue Feb 23 14:08:32 2016] lll
>kk
[Tue Feb 23 14:25:51 2016] kk
四、SocketServer模块
创建SocketServeTCP服务器
[root@localhost opt]# cat -n TsTservss.py
1 #!/usr/bin/python
2
3 from SocketServer import (TCPServer as TCP,StreamRequestHandler as SRH)
4 from time import ctime
5
6 HOST = ''
7 PORT = 21567
8 ADDR = (HOST,PORT)
9
10 class MyRequestHandler(SRH):
11 def handle(self):
12 print '...connected from:',self.client_address
13 self.wfile.write('[%s] %s' % (ctime(),self.rfile.readline()))
14
15 tcpServ = TCP(ADDR,MyRequestHandler)
16 print 'waiting for connection...'
17 tcpServ.serve_forever()
[root@localhost opt]# cat -n TsTclntss.py
1 #!/usr/bin/python
2
3 from socket import *
4
5 HOST = 'localhost'
6 PORT = 21567
7 BUFSIZ = 1024
8 ADDR = (HOST,PORT)
9
10 while True:
11 tcpCliSock = socket(AF_INET,SOCK_STREAM)
12 tcpCliSock.connect(ADDR)
13 data = raw_input('>')
14 if not data:
15 break
16 tcpCliSock.send('%s\r\n' % data)
17 data = tcpCliSock.recv(BUFSIZ)
18 if not data:
19 break
20 print data.strip()
21
运行服务器脚本
[root@localhost opt]# ./TsTservss.py
waiting for connection...
...connected from: ('127.0.0.1', 58325)
...connected from: ('127.0.0.1', 58326)
...connected from: ('127.0.0.1', 58327)
...connected from: ('127.0.0.1', 58328)
运行客户端脚本
[root@localhost opt]# ./TsTclntss.py
>ll
[Tue Feb 23 16:26:18 2016] ll
>mm
[Tue Feb 23 16:26:19 2016] mm
>kk
[Tue Feb 23 16:26:21 2016] kk
>