建议关注: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、 套接字对象方法 下面是最常用的套接字对象方法:
服务器端套接字函数
|
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(接受客户端数据,打上时间戳返回)配置代码如下###############
把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(发送客户端数据,接受时间戳返回)配置代码如下##########
建议:创建线程来处理客户端请求。SocketServer模块是一个基于socket模块的高级别的套接字通信模块,支持新的线程或进程中处理客户端请求。同时建议在退出和调用服务器close()函数时使用try-except语句。
3、创建UDP服务器
服务器 udpSerSock.py
核心操作如下:
ss = socket() # 创建服务器套接字
ss.bind() # 绑定服务器套接字
inf_loop: # 服务器无限循环
cs = ss.recvfrom()/ss.sendto()
# 对话 接受与发送数据
ss.close() # 关闭服务器套接字
客户端 udpCliSock.py
核心操作如下:
cs = socket() # 创建客户端套接字
inf_loop: # 服务器无限循环
cs.sendto()/cs.recvfrom() # 对话 接受与发送数据
cs.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 |
得到/设置默认的套接字超时时间,单位秒(浮点型) |