16.1 引言python
16.1.1 什么是客户端/服务器架构react
服务器是一个软件或者硬件,用于向一个或多个客户端(客户),提供所须要的“服务”。服务器存在的惟一目的就是等待客户的请求,给这些客户服务,而后在等待其余的请求。编程
16.1.2 客户端/服务器网络编程服务器
在完成服务以前,服务器必需要先完成一些设置。先要建立一个通信节点,让服务器能“监听”请求。网络
在网络的世界里,基本上也就是这样—一旦通讯端点建立好以后,咱们在监听的服务器就能够进入它等待和处理客户请求的无限循环中了。数据结构
一样地,服务器在准备好以后,也要通知潜在的客户,让他们知道服务器已经准备好处理服务了,不然没有人会提请求。架构
16.2 套接字:通讯端点框架
套接字是一种具备以前所说的“通讯端点”概念的计算机网络数据结构。网络化的应用程序在开始任何通信以前都必须建立套接字。就像电话的插口同样,没有它就彻底没办法通讯。socket
AF_UNIX, AF_NETLINK,AF_INETtcp
16.2.2 套接字地址:主机与端口
若是把套接字比做电话的插口--即通讯的最底层结构,那主机与端口就像区号与电话号码的一对组合。
合法的端口号范围为0-65535.其中小于1024的端口号为系统保留端口。
16.2.3 面向链接与无链接
1.面向链接
面向链接的套接字,即在通讯以前必定要创建一条链接。这种通讯方式也被称为“虚电路”或“流套接字”。面向链接的通讯方式提供了顺序的、可靠的、不会重复的数据传输,并且也不会被加上数据边界。
这也意味着,每一次要发送的信息,可能会被拆分红多份,每一份都会很少很多地证券到达目的地。而后被从新按顺序拼装起来,传给正在等待的应用程序。
实现这种链接的主要协议就是传输控制协议(即TCP)。要建立TCP套接字就得在建立的时候指定套接字类型为SOCK_STREAM。
TCP套接字采用SOCK_STREAM,表达了它做为流字套接字的特色。因为这些套接字使用网际协议(IP)来查找网络中的主机,因此这样造成的整个系统,通常会由这两个协议(TCP/IP)名的组合来描述,即TCP/IP
2.无链接
与虚电路彻底相反的是数据报型的无链接嵌套字。这意味着,无需链接就能够进行通信,但这时,数据到达的顺序、可靠性及不重复性就没法保证。数据报保留数据边界,数据即是整个发送。
使用数据报来传输数据,不必定按照它们发送的顺序到达。甚至到达不了。
优势是数据报不须要维持虚电路链接,一般能维持更好的性能,更适应某些应用场合
实现这种链接的主要协议就是用户数据报协议(即UDP)。要建立UDP套接字就得在建立的时候指定套接字类型为SOCK_DGRAM。UDP/IP
16.3 Python中的网络编程
16.3.1socket()模块函数
socket(socket_family, socket_type, protocol=0)
socket_family不是AF_VNIX就是AF_INET
socket_type能够是SOCK_STREAM或者SOCK_DGRAM
protocol通常为0
tcpsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
一样的,建立一个UDP/IP套接字
upstock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
使用from socket import* 缩短代码
16.3.2套接字对象(内建)方法
16.3.3建立一个TCP服务器
SocketServer模块是一个基于socket模块的高级别的套接字通信模块,它支持新的线程中处理客户端的请求。
#-*-coding:utf-8-*- from socket import * from time import ctime HOST = '' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) while True: print 'waiting for connecting...' tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' %(ctime(), data)) tcpCliSock.close() tcpSerSock.close()
16.3.4 建立TCP客户端
from socket import * HOST = 'localhost' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) while True: data = raw_input('>') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(BUFSIZ) if not data: break print data tcpCliSock.close()
“友好地”退出的一个方法就是把服务器的无限循环放在一个try-except 语句的try 子句当中,并捕获EOFError 和KeyboardInterrupt 异常。在异常处理子句中,调用close()函数关闭服务器的套接字。
16.3.5 运行咱们的客户端与TCP服务器
16.3.6 建立一个UDP服务器
#-*-coding:utf-8-*- from socket import * from time import ctime HOST = '' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) udpSerSock = socket(AF_INET, SOCK_DGRAM) udpSerSock.bind(ADDR) while True: print 'waiting for connecting...' data, addr = udpSerSock.recvfrom(BUFSIZ) udpSerSock.sendto('[%s] %s' %(ctime(), data),addr) print '...received from and return to:', addr udpSerSock.close()
16.3.7 建立一个UDP客户端
from socket import * HOST = 'localhost' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) udpCliSock = socket(AF_INET, SOCK_DGRAM) while True: data = raw_input('>') if not data: break udpCliSock.sendto(data,ADDR) data,ADDR = udpCliSock.recvfrom(BUFSIZ) if not data: break print data udpCliSock.close()
16.3.8 执行UDP服务器和客户端
16.3.9 Socket模块属性
数据属性
AF_UNIX,AF_INET,AF_INET6
SO_STREAM,SO_DGRAM
异常
error 套接字相关错误
herror 主机和地址相关的错误
gaierror 地址相关的错误
timeout 超时
函数
socket() 用指定的地址家族,套接字类型,和协议类型建立一个套接字对象
16.4 SocketServer模块
SocketSever是标准库中一个高级别的模块,用于简化实现网络客户端和服务器所需的大量的样板代码。该模块中,已经实现了一些可供使用的类。
16.4.1建立一个SocketServerTCP服务器
#-*-coding:utf-8-*- from SocketServer import (TCPServer as TCP, StreamRequestHandler as SRH) from time import ctime HOST = '' PORT = 20000 ADDR = (HOST, PORT) class MyRequestHander(SRH): def handle(self): print '...connected from:', self.client_address self.wfile.write('[%s] %s' %(ctime(),self.rfile.readline())) #建立TCP服务器 tcpServ = TCP(ADDR, MyRequestHander) print 'waiting for connection' tcpServ.serve_forever()
16.4.2建立SocketServerTCP客户端
from socket import * HOST = 'localhost' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) while True: tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) data = raw_input('>') if not data: break tcpCliSock.send('%s\r\n' % data) data = tcpCliSock.recv(BUFSIZ) if not data: break print data.strip() tcpCliSock.close()
16.4.3 执行TCP服务器和客户端
16.5 Twisted 框架介绍
16.5.1建立一个Twisted Reactor TCP服务器
#-*-coding:utf-8-*- from twisted.internet import protocol,reactor from time import ctime PORT = 21332 class TSServProtocol(protocol.Protocol): def connectionMade(self): clnt = self.clnt = self.transport.getPeer().host print '...connected from:', clnt def dataReceived(self, data): self.transport.write('[%s] %s' %(ctime(), data)) factory = protocol.Factory() factory.protocol = TSServProtocol print 'waiting for connection...' reactor.listenTCP(PORT,factory) reactor.run()
16.5.2 建立一个Twisted Reactor TCP客户端
from twisted.internet import protocol, reactor HOST = 'localhost' PORT = 21332 class TSClntProtocol(protocol.Protocol): def sendData(self): data = raw_input('>') if data: print '...sending %s...' %data self.transport.write(data) else: self.transport.loseConnection() def connectionMade(self): self.sendData() def dataReceived(self, data): print data self.sendData() class TSClntFactory(protocol.ClientFactory): protocol = TSClntProtocol clientConnectionLost = clientConnectionFailed = \ lambda self,connector, reason: reactor.stop() reactor.connectTCP(HOST, PORT, TSClntFactory()) reactor.run()
16.5.3执行
16.6 相关模块
16.7 练习
16-1 套接字。面向链接和无链接有什么区别?
面向链接是顺序的、可靠的、不会重复的数据传输
无链接中数据到达的顺序,可靠性及重复性没法保证
16-2 客户端/服务器架构。用你本身的语言描述这个架构,并给出几个例子。
服务器就是用来等待客户的请求,服务客户,客户端就是请求链接服务器,发送数据
16-3套接字。TCP 和UDP 中,哪种服务器在接受链接后,把链接交给不一样的套接字处理与客户的通信。
TC
16-4 修改TCP(tsTclnt.py)和UDP(tsUclnt.py)客户端,让服务器的名字不要在代码里写死,要容许用户指定一个主机名和端口,只有在两个值都没有输入的时候,才使用默认值。
#-*-coding:utf-8-*- from socket import * from time import ctime HOST = raw_input("Please input the host:") PORT = int(raw_input("please enter the port:")) BUFSIZ = 1024 ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) while True: print 'waiting for connecting...' tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr while True: data = tcpCliSock.recv(BUFSIZ) if not data: break tcpCliSock.send('[%s] %s' %(ctime(), data)) tcpCliSock.close() tcpSerSock.close()
from socket import * HOST = raw_input("Please input the host:") PORT = int(raw_input("please input the port:")) BUFSIZ = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) while True: data = raw_input('>') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(BUFSIZ) if not data: break print data tcpCliSock.close()
16-6 日期时间服务。使用socket.getservbyname()函数获得UDP 协议中,“daytime”服务所对应的端口。请参考getservbyname() 函数的文档, 查阅详细的语法。( 即:socket.getservbyname.__doc__)。如今,写一个程序发送一个随便什么数据过去,等待回答。一旦你收到了服务器的信息,显示到屏幕上。
getservbyname("daytime","udp") 运行显示错误permission denied.
16-7 半双工聊天。建立一个简单的,半双工的聊天程序。“半双工”的意思是当建立一个链接,服务启动的时候,只有一我的能够打字,另外一我的只有在等到有消息通知他输入消息时,才能说话。一旦消息发送出去后,要等到有回复了才能发送下一条消息。一我的是服务端,另外一我的是客户端。
#-*-coding:utf-8-*- from socket import * from time import ctime HOST = '' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR) tcpSerSock.listen(5) while True: print 'waiting for connecting...' tcpCliSock, addr = tcpSerSock.accept() print '...connected from:', addr while True: data = tcpCliSock.recv(BUFSIZ) if not data: break print data data1 = raw_input(">>") tcpCliSock.send(data1) tcpCliSock.close() tcpSerSock.close()
from socket import * HOST = 'localhost' PORT = 20000 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) tcpCliSock.connect(ADDR) while True: data = raw_input('>') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(BUFSIZ) if not data: break print data tcpCliSock.close()