基于python的网络编程

来源:互联网 发布:闪电站小猪知乎 编辑:程序博客网 时间:2024/05/16 12:52

建议关注:TCP和UDP的区别、socket其中的参数含义、TCP三次握手及传递的参数、可以写出socket通讯伪代码。

网络编程就是如何在程序中实现两台计算机的通信。更确切地说,网络通信是两台计算机上的两个进程之间的通信。比如,浏览器进程和新浪服务器上的某个Web服务进程在通信,而QQ进程是和腾讯的某个服务器上的某个进程在通信。网络编程对所有开发语言都是一样的,Python也不例外。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。

###########硬件客户端/服务器架构和 软件客户端/服务器架构
        硬件的客户端/服务器架构,例如打印服务器、文件服务器(客户可以远程把服务器的磁盘映射到自己本体并使用);软件客户端/服务器架构主要是程序的运行、数据收发、升级等,最常见的是Web服务器、数据库服务器。如一台机器存放一些网页或Web应用程序,然后启动服务。其服务器的任务就是接受客户端的请求,把网页发给客户端(如用户计算机上的浏览器),然后再等待下一个客户端请求。软件服务器也运行在一块硬件之上,但是没有像硬件服务器那样的专用外围设备(如打印机、磁盘驱动器等)

二、########  硬件客户端/服务器架构和软件客户端/服务器架构

        硬件的客户端/服务器架构,例如打印服务器、文件服务器(客户可以远程把服务器的磁盘映射到自己本体并使用);软件客户端/服务器架构主要是程序的运行、数据收发、升级等,最常见的是Web服务器、数据库服务器。如一台机器存放一些网页或Web应用程序,然后启动服务。其服务器的任务就是接受客户端的请求,把网页发给客户端(如用户计算机上的浏览器),然后再等待下一个客户端请求。

三、################ 套接字Socket
1.什么是套接字
        套接字是一种具有之前所说的“通信端点”概念的计算网络数据结构。相当于电话插口,没它无法通信,这个比喻非常形象。
        套接字起源于20世纪70年代加州伯克利分校版本的Unix,即BSD Unix。又称为“伯克利套接字”或“BSD套接字”。最初套接字被设计用在同一台主机上多个应用程序之间的通讯,这被称为进程间通讯或IPC。
        套接字分两种:基于文件型和基于网络的
        第一个套接字家族为AF_UNIX,表示“地址家族:UNIX”。包括Python在内的大多数流行平台上都使用术语“地址家族”及其缩写AF。由于两个进程都运行在同一台机器上,而且这些套接字是基于文件的,所以它们的底层结构是由文件系统来支持的。可以理解为同一台电脑上,文件系统确实是不同的进程都能进行访问的。
        第二个套接字家族为AF_INET,表示”地址家族:Internet“。还有一种地址家族AF_INET6被用于网际协议IPv6寻址。python 2.5中加入了一种Linux套接字的支持:AF_NETLINK(无连接)套接字家族,让用户代码与内核代码之间的IPC可以使用标准BSD套接字接口,这种方法更为精巧和安全。
        Python只支持AF_UNIX、AF_NETLINK和AF_INET家族。网络编程关注AF_INET。
        如果把套接字比作电话的查看——即通信的最底层结构,那主机与端口就相当于区号和电话号码的一对组合。一个因特网地址由网络通信必须的主机与端口组成。
        而且另一端一定要有人接听才行,否则会提示”对不起,您拨打的电话是空号,请查询后再拨“。同样你也可能会遇到如”不能连接该服务器、服务器无法响应“等。合法的端口范围是0~65535,其中小于1024端口号为系统保留端口。

知名端口号列表: https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml


2.面向连接与无连接
        面向连接:通信之前一定要建立一条连接,这种通信方式也被成为”虚电路“或”流套接字“。面向连接的通信方式提供了顺序的、可靠地、不会重复的数据传输,而且也不会被加上数据边界。这意味着,每发送一份信息,可能会被拆分成多份,每份都会不多不少地正确到达目的地,然后重新按顺序拼装起来,传给正等待的应用程序。
        实现这种连接的主要协议就是传输控制协议TCP。要创建TCP套接字就得创建时指定套接字类型为SOCK_STREAM。TCP套接字这个类型表示它作为流套接字的特点。由于这些套接字使用网际协议IP来查找网络中的主机,所以这样形成的整个系统,一般会由这两个协议(TCP和IP)组合描述,即TCP/IP。
        无连接:无需建立连接就可以通讯。但此时,数据到达的顺序、可靠性及不重复性就无法保障了。数据报会保留数据边界,这就表示数据是整个发送的,不会像面向连接的协议先拆分成小块。它就相当于邮政服务一样,邮件和包裹不一定按照发送顺序达到,有的甚至可能根本到达不到。而且网络中的报文可能会重复发送。
        那么这么多缺点,为什么还要使用它呢?由于面向连接套接字要提供一些保证,需要维护虚电路连接,这都是严重的额外负担。数据报没有这些负担,所有它会更”便宜“,通常能提供更好的性能,更适合某些场合,如现场直播要求的实时数据讲究快等。
        实现这种连接的主要协议是用户数据报协议UDP。要创建UDP套接字就得创建时指定套接字类型为SOCK_DGRAM。这个名字源于datagram(数据报),这些套接字使用网际协议来查找网络主机,整个系统叫UDP/IP。

四、python 中的网络编程

1、socket()模块函数
        使用socket模块的socket()函数来创建套接字。语法如下:
            socket(socket_family, socket_type, protocol=0)
        其中socket_family不是AF_VNIX就是AF_INET,socket_type可以是SOCK_STREAM或者SOCK_DGRAM,protocol一般不填,默认值是0。
        创建一个TCP/IP套接字的语法如下:
             tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        同样创建一个UDP/IP套接字的语法如下:
             udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        由于socket模块中有太多属性,所以使用"from socket import *"语句,把socket模块里面的所有属性都带到命名空间中,较"from module import *"语句大幅缩短代码。调用如下:
             tcpSock = socket(AF_INET, SOCK_STREAM)

2、  套接字对象方法      下面是最常用的套接字对象方法:
                服务器端套接字函数

socket类型

描述

s.bind()

绑定地址(主机号 端口号对)到套接字

s.listen()

开始TCP监听

s.accept()

被动接受TCP客户端连接,(阻塞式)等待连续的到来

       客户端套接字函数

socket类型

描述

s.connect()

主动初始化TCP服务器连接

s.connect_ex()

connect()函数扩展版本,出错时返回出错码而不是跑出异常

       公共用途的套接字函数

socket类型

描述

s.recv()

接受TCP数据

s.send()

发送TCP数据

s.sendall()

完整发送TCP数据

s.recvfrom()

接受UDP数据

s.sendto()

发送UDP数据

s.getpeername()

连接到当前套接字的远端地址(TCP连接)

s.getsockname()

获取当前套接字的地址

s.getsockopt()

返回指定套接字的参数

s.setsockopt()

设置指定套接字的参数

s.close()

关闭套接字

        面向模块的套接字函数

socket类型

描述

s.setblocking()

设置套接字的阻塞与非阻塞模式

s.settimeout()

设置阻塞套接字操作的超时时间

s.gettimeout()

得到阻塞套接字操作的超时时间

        面向文件的套接字函数

socket类型

描述

s.fileno()

套接字的文件描述符

s.makefile()

创建一个与套接字关联的文件对象


                 数据属性:

                 s.family   套接字家族       s.type  :套接字类型    s.proto :套接字协议

###################################实例##############################################

1、创建TCP服务器

  核心操作如下:
        ss = socket()                # 创建服务器套接字
        ss.bind()                   # 地址绑定到套接字上
        ss.listen()                      # 监听连接
             inf_loop:                       # 服务器无限循环
                  cs = ss.accept()       # 接受客户端连接 阻塞式:程序连接之前处于挂起状态
             comm_loop:                 # 通信循环
                  cs.recv()/cs.send()   # 对话 接受与发送数据
             cs.close()                      # 关闭客户端套接字 
             ss.close()                      # 关闭服务器套接字 (可选)

#############TCP时间服务器 tcpSerSock.py(接受客户端数据,打上时间戳返回)配置代码如下###############

[python] view plaincopy
  1. # -*- coding: utf-8 -*-   
  2. from socket import *  
  3. from time import ctime  
  4.   
  5. HOST = 'localhost'          #主机名  
  6. PORT =  21567               #端口号  
  7. BUFSIZE = 1024              #缓冲区大小1K  
  8. ADDR = (HOST,PORT)  
  9.   
  10. tcpSerSock = socket(AF_INET, SOCK_STREAM)  
  11. tcpSerSock.bind(ADDR)       #绑定地址到套接字  
  12. tcpSerSock.listen(5)        #监听 最多同时5个连接进来  
  13.   
  14. while True:                 #无限循环等待连接到来  
  15.     try:  
  16.         print 'Waiting for connection ....'  
  17.         tcpCliSock, addr = tcpSerSock.accept()  #被动接受客户端连接  
  18.         print u'Connected client from : ', addr  
  19.   
  20.         while True:  
  21.             data = tcpCliSock.recv(BUFSIZE)     #接受数据  
  22.             if not data:  
  23.                 break  
  24.             else:  
  25.                 print 'Client: ',data  
  26.             tcpCliSock.send('[%s] %s' %(ctime(),data)) #时间戳  
  27.   
  28.     except Exception,e:  
  29.         print 'Error: ',e  
  30. tcpSerSock.close()          #关闭服务器  

把HTTP头和网页分离一下,把HTTP头打印出来,网页内容保存到文件代码示例:

header, html = data.split('\r\n\r\n', 1)print header# 把接收的数据写入文件:with open('sina.html', 'wb') as f:    f.write(html)

# 创建新线程来处理TCP连接:    t = threading.Thread(target=tcplink, args=(sock, addr))    t.start()

每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接:


2、创建TCP服务器

  核心操作如下:

cs = socket()                 # 创建客户端套接字
                cs.connect()                  # 尝试连接服务器
                comm_loop:                 # 通讯循环
                cs.send()/cs.recv()    # 对话 发送接受数据
                cs.close()                       # 关闭客户端套接字

###########TCP时间戳客户端 tsTclnt.py(发送客户端数据,接受时间戳返回)配置代码如下##########

  1. # -*- coding: utf-8 -*-   
  2. from socket import *  
  3.   
  4. HOST = 'localhost'          #主机名  
  5. PORT =  21567               #端口号 与服务器一致  
  6. BUFSIZE = 1024              #缓冲区大小1K  
  7. ADDR = (HOST,PORT)  
  8.   
  9. tcpCliSock = socket(AF_INET, SOCK_STREAM)  
  10. tcpCliSock.connect(ADDR)    #连接服务器  
  11.   
  12. while True:                 #无限循环等待连接到来  
  13.     try:  
  14.         data = raw_input('>')  
  15.         if not data:  
  16.             break  
  17.         tcpCliSock.send(data)            #发送数据  
  18.         data = tcpCliSock.recv(BUFSIZE)  #接受数据  
  19.         if not data:  
  20.             break  
  21.         print 'Server: ', data  
  22.     except Exception,e:  
  23.         print 'Error: ',e  
  24.           
  25. tcpCliSock.close()          #关闭客户端  

 建议:创建线程来处理客户端请求。SocketServer模块是一个基于socket模块的高级别的套接字通信模块,支持新的线程或进程中处理客户端请求。同时建议在退出和调用服务器close()函数时使用try-except语句。


3、创建UDP服务器

服务器 udpSerSock.py
        核心操作如下:
        ss = socket()                # 创建服务器套接字
        ss.bind()                   # 绑定服务器套接字
             inf_loop:                       # 服务器无限循环
                  cs = ss.recvfrom()/ss.sendto()         
                                                  # 对话 接受与发送数据
             ss.close()                      # 关闭服务器套接字

  1. # -*- coding: utf-8 -*-   
  2. from socket import *  
  3. from time import ctime  
  4.   
  5. HOST = ''                   #主机名  
  6. PORT =  21567               #端口号  
  7. BUFSIZE = 1024              #缓冲区大小1K  
  8. ADDR = (HOST,PORT)  
  9.   
  10. udpSerSock = socket(AF_INET, SOCK_DGRAM)  
  11. udpSerSock.bind(ADDR)       #绑定地址到套接字  
  12.   
  13. while True:                 #无限循环等待连接到来  
  14.     try:  
  15.         print 'Waiting for message ....'  
  16.         data, addr = udpSerSock.recvfrom(BUFSIZE)          #接受UDP  
  17.         print 'Get client msg is: ', data  
  18.         udpSerSock.sendto('[%s] %s' %(ctime(),data), addr) #发送UDP  
  19.         print 'Received from and returned to: ',addr  
  20.   
  21.     except Exception,e:  
  22.         print 'Error: ',e  
  23. udpSerSock.close()          #关闭服务器  

客户端 udpCliSock.py
        核心操作如下:
        cs = socket()                            # 创建客户端套接字
             inf_loop:                                  # 服务器无限循环
                  cs.sendto()/cs.recvfrom()   # 对话 接受与发送数据                                                
             cs.close()                                 # 关闭客户端套接字 

  1. # -*- coding: utf-8 -*-   
  2. from socket import *  
  3.   
  4. HOST = 'localhost'          #主机名  
  5. PORT =  21567               #端口号 与服务器一致  
  6. BUFSIZE = 1024              #缓冲区大小1K  
  7. ADDR = (HOST,PORT)  
  8.   
  9. udpCliSock = socket(AF_INET, SOCK_DGRAM)  
  10.   
  11. while True:                 #无限循环等待连接到来  
  12.     try:  
  13.         data = raw_input('>')  
  14.         if not data:  
  15.             break  
  16.         udpCliSock.sendto(data, ADDR)            #发送数据  
  17.         data,ADDR = udpCliSock.recvfrom(BUFSIZE)  #接受数据  
  18.         if not data:  
  19.             break  
  20.         print 'Server : ', data  
  21.   
  22.     except Exception,e:  
  23.         print 'Error: ',e  
  24.           
  25. udpCliSock.close()          #关闭客户端  


##################Socket模块属性#######################

除了我们已经很熟悉的socket.socket()函数之外,socket模块还有很多属性可供网络应用程序使用。


表 Socket模块属性

属 性 名 字

描    述

数据属性

AF_UNIX, AF_INET, AF_INET6a

Python支持的套接字地址家族

SO_STREAM, SO_DGRAM

套接字类型(TCP = 流,UDP = 数据报)

has_ipv6b

表示是否支持IPv6的布尔型标志

异常

error

套接字相关错误

herrora

主机和地址相关的错误

gaierrora

地址相关的错误

timeoutb

超时

函数

socket()

用指定的地址家族、套接字类型和协议类型创建一个套接字对象(可选)

socketpair()c

用指定的地址家族、套接字类型和协议类型创建一对套接字对象(可选)

fromfd()

用一个已经打开的文件描述符创建一个套接字对象

ssl()d

在套接字上发起一个安全套接字层(SSL)。不做证书验证

getaddrinfo()a

得到地址信息

getfqdn()e

返回完整的域的名字

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()b

把IP地址转为二进制格式或反之(对IPv4和Ipv6地址都有效)

getdefaulttimeout()/setdefaulttimeout()b

得到/设置默认的套接字超时时间,单位秒(浮点型)