Python之网络编程
2018-03-20 本文已影响8人
shenyoujian
一、TCP编程
socket用于打开网络连接,打开一个socket需要知道目标计算机的ip地址和端口号,还有协议类型.大多数的连接都是基于tcp协议连接.tcp连接中,主动发起连接叫客户端,被动响应连接叫服务器.
1、现在我们来创建一个本机与新浪的基于tcp连接的socket
#导入socket库
import socket
#创建一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#建立连接
s.connect(('www.sina.com.cn', 80))
#发送数据
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
2、创建tcp只需要注意的有三点
- 创建socket对象并传入ipv4协议AF_INET和tcp协议SOCK_STREAM两个参数,其中第一个参数可以用更先进的IPV6(AF_INET6)
- socket对象创建成功后,还要由客户端主动发起连接,并且传入一个tuple(服务器的ip地址和端口号),其中ip地址可以用域名自动转换到ip地址.
- 连接成功就是发送请求,上面的请求时要求服务器返回首页的内容,发送的文本格式必须符合http标准
3、如果上面都没问题,接下来就可以接收新浪服务器返回的数据了
#接收数据:
buffer = []
while True:
#每次最多接收1k字节
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
print(buffer) #字节流数组
data = b''.join(buffer)
with open('data.txt', 'wb') as f:
f.write(data)
#关闭数据
s.close()
4、接收数据的点:
- 1、调用recv(max)方法,一次最多接受制定的字节数,我们可以用一个循环去反复接收,直到recv返回空数据.
- 2、接收完后就要关闭socket
5、接收到的数据包括http头和网页本身,我们需要分离它们,我们要的是网页内容
header, html = data.split(b'\r\n\r\n', 1) #以分隔符是二进制的换行回车,分成n+1个字符串并返回list
print(header)
with open('sina.html', 'wb') as f:
f.write(html)
# 输出:
# 你可以在python交互式看到一大串字节,里面是新浪的首页和桌面上的data.txt和sina.html,当你打开它们来看
#它们就会自动解码成utf8.
6、那么在新浪服务器那边是怎么知道有客户端连接过来,接受客户端发送的数据和发送给客户端的数据呢?
首先服务器进程要绑定一个端口并且监听来自其他客户端的连接,如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。然后每个socket都得依赖4项:服务器地址,服务器端口,客户端地址,客户端端口来唯一确定一个socket.(如果有很多客户端的连接)。最后服务器还要分别响应客户端的请求,所以服务器那边要开启多个进程或者线程来处理,否则服务器一次就只能服务一个客户端.
7、例子:
编写一个简单的服务器程序,它接收客户端连接,把客户端发过来的字符串加上Hello再发回去。
服务端:
import threading,time,socket
#第一步:创建一个基于ipv4和tcp协议的socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#第二步绑定监听的地址和端口.监听的地址可以是0.0.0.0上的所有网络地址,也可以是某个网卡的ip地址上,也可以绑定到本机
#这里我们只有一台电脑所以绑定到本机当绑定到本机的时候,这个服务器就只能接收运行在本机的客户端发来的socket连接.
#监听端口
s.bind(('127.0.0.1', 9999)) #注意传入tuple,小于1024才是标准端口.
s.listen(5) #参数5是指等待连接的最大数量
print('waiting for connection....')
#第三步,服务器程序通过一个永久循环接受来自客户端的连接,accept()会等待并返回一个客户端的连接
# 每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接
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('utf8')=='exit':
break
sock.send(('hello, %s!' % data.decode('utf8')).encode('utf8'))
sock.close()
print('connection from %s:%s closed.' % addr)
while True:
#接受一个新链接:
sock, addr = s.accept()
#创建新线程来处理tcp连接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
客户端:
#客户端程序
import socket
#第一步创建一个基于ipv4和tcp协议的socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#第二步建立连接
s.connect(('127.0.0.1',9999))
#第三步先读取服务器传过来的欢迎信息的字节流数据
print(s.recv(1024).decode('utf8'))
#第四步发送数据
for data in [b'Michael', bytes('小明', 'utf8')]:
s.send(data)
# 第五步接收数据
print(s.recv(1024).decode('utf8'))
#最后断开连接
s.send(b'exit')
s.close()
运行截图:
5.png
二、UDP编程
1、udp协议是面向无连接的协议,则你是要udp协议时,是不需要建立连接的,之需要知道对方的ip地址和端口号,但是不一定能到达,但相对tcp速度更快.
2、服务端
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #sock_dgram指定了socket类型是udp
#绑定端口:
s.bind(('127.0.0.1', 9999))
#不需要调用listen方法
print('bind udp on 9999...')
while True:
#接收数据:
data, addr = s.recvfrom(1024) #recvfrom方法返回客户端的数据和地址和端口
print('received from %s:%s.' %addr)
s.sendto(b'hello, %s!' %data, addr) #sendto向客户端发送数据
3、客户端
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', bytes('小明', 'utf8')]:
#发送数据
s.sendto(data, ('127.0.0.1', 9999))
#接收数据
print(s.recv(1024).decode('utf8')) #从服务端接收数据仍然用recv方法
s.close()
三、总结:
1、tcp和udp都是用于网络传输的协议,udp不用建立连接更快但是数据有可能传输不到。
2、tcp服务端的步骤是:
- 使用socket创建一个基于ipv4和tcp协议的socket
- 使用bind绑定端口
- 使用listen监听端口
- 使用send发送数据
- 使用recv接收数据
- 使用close断开连接
3、tcp客户端的步骤是: - 使用socket创建一个基于ipv4和tcp协议的socket
- 使用connect建立连接
- 使用send发送数据
- 使用recv接收数据
- 使用close断开连接
4、udp服务端的步骤是: - 使用socket创建一个基于ipv4和udp协议的socket
- 使用bind绑定端口
- 使用recvfrom接收数据
- 使用sendto发送数据
- 使用close来断开连接
5、udp客户端的步骤是: - 使用socket创建一个基于ipv4和udp协议的socket
- 使用bind绑定端口
- 使用recv接收数据
- 使用sendto发送数据
- 使用close来断开连接
6、服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。
最后附上一个歌,卡妹的real friends。