Python网络编程(一)
一、网络基础
在学习网络编程前,要对网络通信的五层协议有所了解,那什么是协议呢,协议就是各方规定遵守的一种标准。
网络通信就像寄信件,是信息与数据的交换,而在生活中我们寄信件,信件也不是从我们手里瞬间到收件人手里,每一次信件通信,都会这样几个固定流程:写信、装信封、投到邮箱、邮局取件、运输到目的地邮局、目的地邮局根据详细地址派送、收件人收件、拆信封、读信。
网络通信也是同样的道理,数据的传输总有一定的流程:发送端程序将数据打包,给数据包印上目标地址,将数据包交给网关,通过路由转发到达目的网络,目的网络网关在根据详细地址分发、目的主机接收数据、拆包、读数据。
这中间我们按照每部分负责的任务的不同,将整个通信划分为五层(分法不唯一,这是最易理解的一种),由下到上分别是物理层、数据链路层、网络层、运输层、应用层,每一层都将较下的一层完全封装,消除通信双方软硬件的差别,而每一层又有约定好的规则以和对方通信,我们这篇文章所要讲的,就是发生在运输层的通信。
我们先来思考一个问题:一台主机的一个进程发送数据时,是如何标识它是发送给目标主机的哪个进程呢?目标主机接受到数据时,由如何判断这份数据是发送给本机的哪个进程呢?
这就是运输层的任务,也就是说,运输层负责两个主机中的应用进程之间的通信。那么上面的问题到底是怎么解决的呢?这就要提到一个新的概念:套接字。
套接字的概念很简单,每台主机有一个唯一的主机地址标识,同时主机内还有标识自己进程的序号id,称作端口,将这两个标识符结合就构成了一个套接字(socket),这个套接字能唯一标识网络中的一个进程。接下来我们的网络编程,都是围绕这个套接字展开的。
运输层的协议主要有UDP和TCP协议,这两个协议都是为了解决进程通信,它们的区别主要是这几点:UDP是无连接、不可靠协议,TCP是有连接、可靠协议,连接指的是通信前要建立连接(连接提供了许多功能:确认、流量控制、连接管理等),可靠指的是如果数据未抵达有对应处理(不可靠则指只负责发送数据)。
这样就了解的差不多了,当然,关于运输层的技术还有很多,难点重点都没有提到,有兴趣的同学可以自己再学习。
二、通信模式
对我们来说,进行网络通信的目的是为了获取信息,而互联网上的信息需求者远远多于信息提供者,不止如此,信息的提供者必须24小时立即回复需求者的请求,这样就产生了客户服务器模式。客户服务器模式是一个逻辑概念,它将主机分为两类,等待请求的是服务器端、发起请求的是客户端,所以服务器端的套接字是必须是公开的,客户端的套接字可以在请求时给服务器端。
三、实现
对UDP来说:因为不需要建立连接,所以只需要在两台主机上各构造一个socket,就能直接发送和接收数据了。
对TCP来说:首先需要建立连接,一个连接就是一对手动绑定的socket,连接成功后就能收发数据了。
结合客户服务端模式,对同一种协议,socket的构造方法也按客户端和服务器分为两种。
话不多说,上代码!
这是基于TCP协议的:
#socket_server_tcp # import socket from socket import * ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_server=socket(AF_INET,SOCK_STREAM) tcp_server.bind(ip_port) tcp_server.listen(back_log) while True: print('服务端开始运行了') conn,addr=tcp_server.accept() #服务端阻塞 print('双向链接是',conn) print('客户端地址',addr) while True: try: data=conn.recv(buffer_size) print('客户端发来的消息是',data.decode('utf-8')) conn.send(data.upper()) except Exception: break conn.close() tcp_server.close()
#socket_client_tcp # import socket from socket import * ip_port=('127.0.0.1',8080) back_log=5 buffer_size=1024 tcp_client=socket(AF_INET,SOCK_STREAM) tcp_client.connect(ip_port) while True: msg=input('>>: ').strip() if not msg:continue tcp_client.send(msg.encode('utf-8')) print('客户端已经发送消息') data=tcp_client.recv(buffer_size) print('收到服务端发来的消息',data.decode('utf-8')) tcp_client.close()
接下来是基于UDP协议的:
# socket_server_udp from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_server=socket(AF_INET,SOCK_DGRAM) #数据报 udp_server.bind(ip_port) while True: data,addr=udp_server.recvfrom(buffer_size) print('收到来自{}的消息:{}'.format(addr, data)) udp_server.sendto(data.upper(),addr)
# socket_client_udp from socket import * ip_port=('127.0.0.1',8080) buffer_size=1024 udp_client=socket(AF_INET,SOCK_DGRAM) #数据报 while True: msg=input('>>: ').strip() udp_client.sendto(msg.encode('utf-8'),ip_port) data,addr=udp_client.recvfrom(buffer_size) # print(data.decode('utf-8')) print('收到服务端发来的消息', data)
运行下试试!
这两对代码已经能实现基本的功能,但还有许多功能没有实现,如TCP版不能同时连接多用户,UDP版有毡包问题(一次接收的数据如果大于缓存区大小就会把剩下的内容附加到下次接受的开头),这些我们放到下次来讲。