Python 网络编程
- 0. 框架
- 1. 理论
- 1.1 进程/线程
- 1.2 网络编程
- 1.3 互联网协议:`TCP /IP协议`
- `ip 地址`
- `TCP 协议`
- 2. `TCP 编程`
- 2.1 概述:客户端和服务器
- 2.2 客户端编程
- 2.3 服务器编程
0. 框架
Socket是一个抽象概念,一个应用程序通过一个Socket来建立一个远程连接,而Socket内部通过TCP/IP协议把数据传输到网络
使用Socket进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它必须主动连接服务器的IP地址和指定端口,如果连接成功,服务器端和客户端就成功地建立了一个TCP连接,双方后续就可以随时发送和接收数据。
一个Socket就是由IP地址和端口号(范围是0~65535)组成,可以把Socket简单理解为IP地址加端口号。
因此,当Socket连接成功地在服务器端和客户端之间建立后:
- 对服务器端来说,它的Socket是指定的IP地址和指定的端口号;
- 对客户端来说,它的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号。
1. 理论
1.1 进程/线程
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。
操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。
1.2 网络编程
计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信。
网络编程就是如何在程序中实现两台计算机的通信。
更确切地说,网络通信是两台计算机上的两个进程之间的通信。
网络编程对所有开发语言都是一样的,Python也不例外。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。
1.3 互联网协议:TCP /IP协议
ip 地址
(1)ip 地址
是什么
互联网上每个计算机的唯一标识就是IP地址,类似123.123.123.123
。
如果一台计算机同时接入到两个或更多的网络,比如路由器,它就会有两个或多个IP地址,所以,IP地址对应的实际上是计算机的网络接口,通常是网卡。
IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地址如192.168.0.1
实际上是把32位整数按8位分组后的数字表示,目的是便于阅读。
(2)ip 地址
的作用
IP协议负责把数据从一台计算机通过网络发送到另一台计算机。
数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。
IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
TCP 协议
TCP(Transmission Control Protocol)传输控制协议
TCP协议
则是建立在IP协议
之上的。
TCP协议
负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。
TCP协议
会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
许多常用的更高级的协议都是建立在TCP协议
基础上的,比如用于浏览器的HTTP协议
、发送邮件的SMTP协议
等。
一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
端口有什么作用?在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
2. TCP 编程
Socket
是网络编程的一个抽象概念。通常我们用一个Socket
表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。
2.1 概述:客户端和服务器
大多数连接都是可靠的TCP连接
。
创建TCP连接
时,主动发起连接的叫客户端,被动响应连接的叫服务器。
比如用浏览器访问新浪,个人计算机——>客户端;新浪服务器——>服务器。
浏览器会主动向新浪的服务器发起连接。如果一切顺利,新浪的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。
2.2 客户端编程
生成的 sina.html 文件
可以直接进入新浪。
注意:
- 新浪强制HTTPS协议访问 所以 80端口改443 socket 改 ssl
import socket
import ssl
# 创建一个socket:
#s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # AF_INET指定使用IPv4协议(如果要用更先进的IPv6,就指定为AF_INET6);SOCK_STREAM指定使用面向流的TCP协议
s = ssl.wrap_socket(socket.socket())
s.connect(('www.sina.com.cn', 443)) # 参数是一个tuple,包含地址和端口号
# 发送数据:
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
# 接收数据:
buffer = []
while True:
# 每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
# 关闭连接:
s.close()
header, html = data.split(b'\r\n\r\n', 1)
print(header.decode('utf-8'))
# 把接收的数据写入文件:
with open('sina.html', 'wb') as f:
f.write(html) # 生成一个 sina.html文件
2.3 服务器编程
分别在不同的两个终端,先后执行server.py
和client.py
,最后会在客户端出现以下结果:
- 服务器端(server.py)
# -*- coding: utf-8 -*-
#server.py
import socket,threading,time
#创建一个基于IPv4和TCP协议的Socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 监听端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
#每个连接都必须创建新线程(或进程)来处理,
#否则,单线程在处理连接的过程中,无法接受其他客户端的连接:
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
print('Connection from %s:%s closed.' % addr)
#服务器程序通过一个永久循环来接受来自客户端的连接,
#accept()会等待并返回一个客户端的连接:
while True:
# 接受一个新连接:
sock, addr = s.accept()
# 创建新线程来处理TCP连接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
- 客户端(client.py)
# -*- coding: utf-8 -*-
#client.py
#要测试这个服务器程序,我们还需要编写一个客户端程序:
# 导入socket库:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect(('127.0.0.1', 9999))
# 接收欢迎消息:
print(s.recv(1024).decode('utf-8'))
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据:
s.send(data)
print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()
附:
服务器可能有多块网卡,可以绑定到某一块网卡的IP地址上,也可以用
0.0.0.0
绑定到所有的网络地址,还可以用127.0.0.1
绑定到本机地址。127.0.0.1
是一个特殊的IP地址,表示本机地址,如果绑定到这个地址,客户端必须同时在本机运行才能连接,也就是说,外部的计算机无法连接进来。端口号需要预先指定。因为我们写的这个服务不是标准服务,所以用
9999
这个端口号。请注意,小于1024
的端口号必须要有管理员权限才能绑定.
参考:
本文主要是参考 网络编程- 廖雪峰的官方网站 、TCP编程所做的一些记录。