socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
1.2.套接字分类这个世界上有很多种套接字(socket),比如 DARPA Internet 地址(Internet 套接字)、本地节点的路径名(Unix套接字)、CCITT X.25地址(X.25 套接字)等。
1.3.Internet 套接分类Internet 套接字分成两种类型:
流格式套接字(Stream Sockets)也叫“面向连接的套接字”,在代码中使用 SOCK_STREAM 表示。
数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。
1.4.无连接套接字数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。
计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。
因为数据报套接字所做的校验工作少,所以在传输效率方面比流格式套接字要高。
可以将 SOCK_DGRAM 比喻成高速移动的摩托车快递,它有以下特征:
另外,用两辆摩托车分别发送两件包裹,那么接收者也需要分两次接收,所以“数据的发送和接收是同步的”;换句话说,接收次数应该和发送次数相同。
总之,数据报套接字是一种不可靠的、不按顺序传递的、以追求速度为目的的套接字。
数据报套接字也使用 IP 协议作路由,但是它不使用 TCP 协议,而是使用 UDP 协议(User Datagram Protocol,用户数据报协议)。
QQ 视频聊天和语音聊天就使用 SOCK_DGRAM 来传输数据,因为首先要保证通信的效率,尽量减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也可以正常解析,最多出现噪点或杂音,不会对通信质量有实质的影响。1.5.有连接套接字
SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。
SOCK_STREAM 有以下几个特征:
数据在传输过程中不会消失;数据是按照顺序传输的;数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。为什么流格式套接字可以达到高质量的数据传输呢?这是因为它使用了 TCP 协议(The Transmission Control Protocol,传输控制协议),TCP 协议会控制你的数据按照顺序到达并且没有错误。
你也许见过 TCP,是因为你经常听说“TCP/IP”。TCP 用来确保数据的正确性,IP(Internet Protocol,网络协议)用来控制数据如何从源头到达目的地,也就是常说的“路由”。
可以将 SOCK_STREAM 比喻成一条传送带,只要传送带本身没有问题(不会断网),就能保证数据不丢失;同时,较晚传送的数据不会先到达,较早传送的数据不会晚到达,这就保证了数据是按照顺序传递的。
那么,“数据的发送和接收不同步”该如何理解呢?
假设传送带传送的是水果,接收者需要凑齐 100 个后才能装袋,但是传送带可能把这 100 个水果分批传送,比如第一批传送 20 个,第二批传送 50 个,第三批传送 30 个。接收者不需要和传送带保持同步,只要根据自己的节奏来装袋即可,不用管传送带传送了几批,也不用每到一批就装袋一次,可以等到凑够了 100 个水果再装袋。
流格式套接字的内部有一个缓冲区(也就是字符数组),通过 socket 传输的数据将保存到这个缓冲区。接收端在收到数据后并不一定立即读取,只要数据不超过缓冲区的容量,接收端有可能在缓冲区被填满以后一次性地读取,也可能分成好几次读取。
也就是说,不管数据分几次传送过来,接收端只需要根据自己的要求读取,不用非得在数据到达时立即读取。传送端有自己的节奏,接收端也有自己的节奏,它们是不一致的。
面向连接的套接字通信工作流程
(1)服务器先用socket函数来建立一个套接字,用这个套接字完成通信的监听
(2)用bind函数来绑定一个端口号和IP地址。因为本地计算机可能有多个IP,每一个IP有多个端口号,需要指定一个IP和端口进行监听
(3)服务器调用Listen函数,使服务器的这个端口和IP出于监听状态,等待客户机的连接
(4)客户机用socket建立一个套接字
(5)客户机调用connect函数,通过远程IP和端口号连接远程计算机指定的端口
(6)服务器用accept函数来接收远程计算机的连接,建立起与客户端之间的通信
(7)建立连接以后,客户机用write函数向socket中写入数据。也可用read函数读取服务器发送来的数据
(8)服务器用read函数读取客户机发送来的数据,也可用write函数发送数据
(9)完成通信以后,用close函数关闭socket连接
要创建套接字,必须使用socket.socket()函数。
form socket import *tcpsock = socket(AF_INTE, SOCK_STREMA)2.2.套接字对象(内置)方法
常见的套接字对象方法和属性
名 称 | 描 述 |
服务器套接字方法 | |
s.bind() | 将地址(主机名、端口号对)绑定到套接字上 |
s.Listen() | 设置并启动 TCP 监听器 |
s.accept() | 被动接受 TCP 客户端连接,一直等待直到连接到达(阻塞) |
客户端套接字方法 | |
s.connect() | 主动发起 TCP 服务器连接 |
s.connect_ex() | connect()的扩展版本,此时会以错误码的形式返回问题,而不是抛出一个异常 |
普通的套接字方法 | |
s.recv() | 接收 TCP 消息 |
s.recv_into()① | 接收 TCP 消息到指定的缓冲区 |
s.send() | 发送 TCP 消息 |
s.sendall() | 完整地发送 TCP 消息 |
s.recvfrom() | 接收 UDP 消息 |
s.recvfrom_into()① | 接收 UDP 消息到指定的缓冲区 |
s.sendto() | 发送 UDP 消息 |
s.getpeername() | 连接到套接字(TCP)的远程地址 |
s.getsockname() | 当前套接字的地址 |
s.getsockopt() | 返回给定套接字选项的值 |
s.setsockopt() | 设置给定套接字选项的值 |
s.shutdown() | 关闭连接 |
s.close() | 关闭套接字 |
s.detach()② | 在未关闭文件描述符的情况下关闭套接字,返回文件描述符 |
s.ioctl()③ | 控制套接字的模式(仅支持 windows) |
面向阻塞的套接字方法 | |
s.setblocking() | 设置套接字的阻塞或非阻塞模式 |
s.settimeout()④ | 设置阻塞套接字 *** 作的超时时间 |
s.gettimeout()④ | 获取阻塞套接字 *** 作的超时时间 |
面向文件的套接字方法 | |
s.fileno() | 套接字的文件描述符 |
s.makefile() | 创建与套接字关联的文件对象 |
数据属性 |
|
s.family① | 套接字家族 |
s.type① | 套接字类型 |
s.proto① | 套接字协议 |
2.3执行TCP服务器和客户端
服务器:
#!/use/bin/env pythonfrom socket import *import timeHOST = ''PORT = 21567BUFSIZE = 1024ADDR = (HOST, PORT)tcpserSock = socket(AF_INET)tcpserSock.bind(ADDR)tcpserSock.Listen(5)while True: print 'waiting to connection...' tcpCliSock, addr = tcpserSock.accept() print '....connected from:',addr while True: data = tcpCliSock.recv(BUFSIZE) if not data: break lotime = time.strftime("%Y-%m-%d %H:%M:%s", time.localtime()) tcpCliSock.send('[%s] %s' % (lotime, data)) tcpCliSock.close()tcpserSock.close()
客户端:
#!/use/bin/env pythonfrom socket import *HOST = 'localhost'PORT = 21567BUFSIZE = 1024ADDR = (HOST, PORT)tcpCliSock = socket(AF_INET)tcpCliSock.connect(ADDR)while True: data = raw_input('> ') if not data: break tcpCliSock.send(data) data = tcpCliSock.recv(BUFSIZE) if not data: break print datatcpCliSock.close()
2.4.UDP服务器和客户端
服务器:
#!/use/bin/env pythonfrom socket import *import timeHOST = ''PORT = 21567BUFSIZE = 1024ADDR = (HOST, PORT)udpSerSock = socket(AF_INET, SOCK_DGRAM)udpSerSock.bind(ADDR)while True: print('waittinng for meaasge...') data, addr = udpSerSock.recvfrom(BUFSIZE) lotime = time.strftime("%Y-%m-%d %H:%M:%s", time.localtime()) udpSerSock.sendto(b'[%s] %s' %(lotime, data), addr) print('...received from and returned to: ', addr) udpSerSock.close()
客户端:
#!/use/bin/env pythonfrom socket import *HOST = 'localhost'PORT = 21567BUFSIZE = 1024ADDR = (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(BUFSIZE) if not data: break print(data.decode('utf-8'))udpCliSock.close()
2.5.socket模块属性
除了属性的socket.socket()函数外,socket()模块还提供下面常见属性:
socket 模块属性
属 性 名 称 | 描 述 |
数据属性 | |
AF_UNIX、AF_INET、AF_INET6①、AF_NETlink②、AF_TIPC③ | Python 中支持的套接字地址家族 |
SO_STREAM、SO_DGRAM | 套接字类型(TCP=流,UDP=数据报) |
has_ipv6④ | 指示是否支持 IPv6 的布尔标记 |
异常 | |
error | 套接字相关错误 |
herror① | 主机和地址相关错误 |
gaIError① | 地址相关错误 |
timeout | 超时时间 |
函数 | |
socket() | 以给定的地址家族、套接字类型和协议类型(可选)创建一个套接字对象 |
socketpair()⑤ | 以给定的地址家族、套接字类型和协议类型(可选)创建一对套接字对象 |
create_connection() | 常规函数,它接收一个地址(主机名,端口号)对,返回套接字对象 |
fromfd() | 以一个打开的文件描述符创建一个套接字对象 |
ssl() | 通过套接字启动一个安全套接字层连接;不执行证书验证 |
getaddrinfo()① | 获取一个五元组序列形式的地址信息 |
getnameinfo() | 给定一个套接字地址,返回(主机名,端口号)二元组 |
getfqdn()⑥ | 返回完整的域名 |
gethostname() | 返回当前主机名 |
gethostbyname() | 将一个主机名映射到它的 IP 地址 |
gethostbyname_ex() | gethostbyname()的扩展版本,它返回主机名、别名主机集合和 IP 地址列表 |
gethostbyaddr() | 将一个 IP 地址映射到 DNS 信息;返回与 gethostbyname_ex()相同的 3 元组 |
getprotobyname() | 将一个协议名(如‘tcp’)映射到一个数字 |
getservbyname()/getservbyport() | 将一个服务名映射到一个端口号,或者反过来;对于任何一个函数来说,协议名都是可选的 |
ntohl()/ntohs() | 将来自网络的整数转换为主机字节顺序 |
htonl()/htons() | 将来自主机的整数转换为网络字节顺序 |
inet_aton()/inet_ntoa() | 将 IP 地址八进制字符串转换成 32 位的包格式,或者反过来(仅用于 IPv4 地址) |
inet_pton()/inet_ntop() | 将IP 地址字符串转换成打包的二进制格式,或者反过来(同时适用于 IPv4 和IPv6 地址) |
getdefaulttimeout()/setdefaulttimeout() | 以秒(浮点数)为单位返回默认套接字超时时间;以秒(浮点数)为单位设置默认套接字超时时间 |
2.6.socketServer模块
虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较 好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也 是Python标准库中很多服务器框架的基础。
socketserver在python2中为SocketServer,在python3种取消了首字母大写,改名为socketserver。
socketserver中包含了两种类,一种为服务类(server class),一种为请求处理类(request handle class)。前者提供了许多方法:像绑定,监听,运行…… (也就是建立连接的过程) 后者则专注于如何处理用户所发送的数据(也就是事务逻辑)。
**一般情况下,所有的服务,都是先建立连接,也就是建立一个服务类的实例,然后开始处理用户请求,也就是建立一个请求处理类的实例。
SocketServer 模块类
类 | 描 述 |
BaseServer | 包含核心服务器功能和mix-in 类的钩子;仅用于推导,这样不会创建这个类的实例;可以用 Tcpserver 或 UDPServer 创建类的实例 |
Tcpserver/UDPServer | 基础的网络同步 TCP/UDP 服务器 |
UnixStreamServer/UnixDatagramServer | 基于文件的基础同步 TCP/UDP 服务器 |
ForkingMixIn/ThreadingMixIn | 核心派出或线程功能;只用作 mix-in 类与一个服务器类配合实现一些异步性;不能直接实例化这个类 |
ForkingTcpserver/ForkingUDPServer | ForkingMixIn 和 Tcpserver/UDPServer 的组合 |
ThreadingTcpserver/ThreadingUDPServer | ThreadingMixIn 和 Tcpserver/UDPServer 的组合 |
BaseRequestHandler | 包含处理服务请求的核心功能;仅仅用于推导,这样无法创建这个类的实例; 可以使用StreamRequestHandler 或 DatagramRequestHandler 创建类的实例 |
StreamRequestHandler/DatagramRequestHandler | 实现 TCP/UDP 服务器的服务处理器 |
服务端:
#!/use/bin/env python# -*- Coding: utf-8 -*-from SocketServer import (Tcpserver as TCP, StreamRequestHandler as SRH)import timeHOST = ''PORT = 21567BUFSIZE = 1024ADDR = (HOST, PORT)#重写SocketServer的子类StreamRequestHandler的handle方法,该方法默认没有任何行为class MyRequestHandler(SRH): def handle(self): print '...connected from:', self.clIEnt_address lotime = time.strftime("%Y-%m-%d %H:%M:%s", time.localtime()) #readline()来获取客户端消息,write()将字符串发回客户端 self.wfile.write('[%s] %s' % (lotime, self.rfile.readline()))#创建TCP服务器,并无限循环的等待客户端请求tcpserv = TCP(ADDR, MyRequestHandler)print 'waiting for conntion....'tcpserv.serve_forever()
客户端:
#!/use/bin/env python# -*- Coding: utf-8 -*-from socket import *HOST = 'localhost'PORT = 21567BUFSIZE = 1024ADDR = (HOST, PORT)while True: tcpCliSock = socket(AF_INET) tcpCliSock.connect(ADDR) data = raw_input('> ') if not data: break tcpCliSock.send('%s\r\n' % data) data = tcpCliSock.recv(BUFSIZE) if not data: break print data.strip() tcpCliSock.close()
2.7.Twisted框架
twisted是一个用python语言写的事件驱动的网络框架,他支持很多种协议,包括UDP,TCP,TLS和其他应用层协议,比如http,SMTP,NNTM,IRC,XMPP/Jabber。 非常好的一点是twisted实现和很多应用层的协议,开发人员可以直接只用这些协议的实现。其实要修改Twisted的SSH服务器端实现非常简单。很多时候,开发人员需要实现protocol类。
一个Twisted程序由reactor发起的主循环和一些回调函数组成。当事件发生了,比如一个clIEnt连接到了server,这时候服务器端的事件会被触发执行。
安装方法:
进入链接https://pypi.org/simple/twisted/下载安装包进行安装。
方法二:
sudo apt-get install python-setuptoolssudo apt-get install python-devsudo easy_install twisted
服务端:#!/use/bin/env python
# -*- Coding: utf-8 -*-from twisted.internet import protocol, reactorimport timePORT = 21567
#获得protocol类并为时间戳服务器调用TSServProtocol,然后重写了connetctionMade()和dataReceived()方法
class TSServProtocol(protocol.Protocol):
#当客户端连接到服务器时就执行connectionMade() def connectionMade(self): clnt =self.clnt = self.transport.getPeer().host print '...connected from:', clnt
#当服务器接收到客户端请求时执行dataReceived() def dataReceived(self, data): lotime = time.strftime("%Y-%m-%d %H:%M:%s", time.localtime()) self.transport.write('[%s] %s' % (lotime, data))factory = protocol.Factory()factory.protocol = TSServProtocolprint 'waiting for connection ...'reactor.ListenTCP(PORT, factory)reactor.run()
客户端:
#!/use/bin/env python# -*- Coding: utf-8 -*-from twisted.internet import protocol, reactorHOST = 'localhost'PORT = 21567class 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 = clIEntConnectionFalIEd = lambda self, connector, reason: reactor.stop()reactor.connectTCP(HOST, PORT, TSClntFactory())reactor.run()
总结
以上是内存溢出为你收集整理的python-网络编程全部内容,希望文章能够帮你解决python-网络编程所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)