当前位置: 移动技术网 > IT编程>脚本编程>Python > python-网络编程

python-网络编程

2019年11月30日  | 移动技术网IT编程  | 我要评论

小萝莉的发育图,付卫东之子,2016辽宁春晚六小龄童

 一:socket和套接字

1.1.什么是socket 

  socket 的原意是“插座”,在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

1.2.套接字分类

       这个世界上有很多种套接字(),比如 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连接

二:python中的网络编程

2.1.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 python

from socket import *
import time

host = ''
port = 21567
bufsize = 1024
addr = (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 python

from socket import *

host = 'localhost'
port = 21567
bufsize = 1024
addr = (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 data

tcpclisock.close()

  

2.4.udp服务器和客户端

服务器:

#!/use/bin/env python

from socket import *
import time

host = ''
port = 21567
bufsize = 1024
addr = (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 python

from socket import *

host = 'localhost'
port = 21567
bufsize = 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(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 time

host = ''
port = 21567
bufsize = 1024
addr = (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 = 21567
bufsize = 1024
addr = (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,这时候服务器端的事件会被触发执行。

安装方法:

进入链接下载安装包进行安装。

方法二:

sudo apt-get install python-setuptools
sudo apt-get install python-dev
sudo easy_install twisted  

 

服务端:#!/use/bin/env python

# -*- coding: utf-8 -*-

from twisted.internet import protocol, reactor
import time

port = 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 = tsservprotocol print 'waiting for connection ...' reactor.listentcp(port, factory) reactor.run()

  

客户端:

#!/use/bin/env python
# -*- coding: utf-8 -*-

from twisted.internet import protocol, reactor

host = 'localhost'
port = 21567

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 = clientconnectionfalied = lambda self, connector, reason: reactor.stop()

reactor.connecttcp(host, port, tsclntfactory())
reactor.run()

  

  

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网