python3学习

Python3(14) Python 网络编程

2018-02-27  本文已影响446人  猿来如痴

本系列主要学习Python的基本使用和语法知识,后续可能会围绕着AI学习展开。
Python3 (1) Python语言的简介
Python3 (2) Python语法基础
Python3 (3) Python函数
Python3 (4) Python高级特性
Python3 (5) Python 函数式编程
Python3 (6) Python 模块
Python3 (7) Python 面向对象编程
Python3 (8) Python 面向对象高级编程
Python3 (9) Python 错误、调试和测试
Python3 (10) Python IO编程
Python3 (11) Python 进程和线程
Python3 (12) Python 常用内建模块
Python3 (13) Python 常用第三方模块
Python3 (14) Python 网络编程

首先说一下最近要忙别的事情,Python 学习可能会中断一小段时间,不过基础篇已经差不多学习完了,再次开始学习的时候主要以项目的形式学习,穿插剩余的知识点,Python (13) 常见的第三方模块 还没完成,之后会写完发出,今天主要是学习 Python 的网络编程,顺便复习一下网络编程这块的知识。

网络编程

网络编程就是如何在程序中实现两台计算机的通信,现在的应用基本上都需要网络,单机应用基本上很少,所以网络编程是一个非常重要又非常基础的知识点,并且网络编程对所有开发语言都是一样的,Python也不例外。用Python进行网络编程,就是在Python程序本身这个进程内,连接别的服务器进程的通信端口进行通信。

TCP/IP简介

从互联网说起,之所以现在互联网无处不在,就是因为计算机网络在发展过程中形成了一套全球通用的协议,互联网协议簇(Internet Protocol Suite)就是通用协议标准。因为互联网协议包含了上百种协议标准,但是最重要的两个协议是TCP和IP协议,所以,大家把互联网的协议简称TCP/IP协议。

TCP编程

Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。 创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# 导入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')
# 接收数据:
buffer = []
while True:
    # 每次最多接收1k字节:
    d = s.recv(1024)
    if d:
        buffer.append(d)
    else:
        break
data = b''.join(buffer)
# 关闭连接:
s.close()
# 分离HTTP头和网页
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文件,打开就是新浪首页。每一步操作都备注了,可以看出客户端要发起一个网络请求需要 5 步,创建 socket 时需要指定是那种协议,SOCK_STREAM指定使用面向流的TCP协议。接受完数据后要记得关闭 socket。

#server
# -*- coding: utf-8 -*-
import socket, threading, time
#TCP socket based on ipv4
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#listening on port
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)

while True:
    # receive a new connect
    sock, addr = s.accept()
    # make a new thread dispose TCPlink
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()
#client
# -*- coding:utf-8 -*-
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#establish connection
s.connect(('127.0.0.1', 9999))
#receive a welcome message
print(s.recv(1024).decode('utf-8'))
for data in [b'Lambda', b'Bond', b'alpha']:
    #send data
    s.send(data)
    print(s.recv(1024).decode('utf-8'))
s.send(b'exit')
s.close()

输出结果:

#server:
Waiting for connection...
Accept new connection from 127.0.0.1:64705...
Connection from 127.0.0.1:64705 closed.
#client:
Welcome!
Hello, Lambda!
Hello, Bond!
Hello, alpha!

以上完成了一个客户端使用TCP协议发出请求,服务器接收处理,返回客户端信息的完整过程。以下是TCP编程的特点:

UDP编程

TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

直接上代码:

#server
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 绑定端口:
s.bind(('127.0.0.1', 9999))

print('Bind UDP on 9999...')

while True:
    # 接收数据:
    data, addr = s.recvfrom(1024)
    print('Received from %s:%s.' % addr)
    reply = 'Hello, %s!' % data.decode('utf-8')
    s.sendto(reply.encode('utf-8'), addr)
#Client
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

for data in [b'Michael', b'Tracy', b'Sarah']:
    # 发送数据:
    s.sendto(data, ('127.0.0.1', 9999))
    # 接收数据:
    print(s.recv(1024).decode('utf-8'))

s.close()

输出结果:

#server:
Bind UDP on 9999...
Received from 127.0.0.1:56838.
Received from 127.0.0.1:56838.
Received from 127.0.0.1:56838.
#client:
Hello, Michael!
Hello, Tracy!
Hello, Sarah!

以上就是UDP协议的使用,UDP的使用与TCP类似,但是不需要建立连接。此外,服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。以下是几个UDP编程的特点:

TCP三次握手与四次挥手机制

所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。

三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的序列号和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。

第一次握手(SYN=1, seq=x):

客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。

发送完毕后,客户端进入 SYN_SEND 状态。

第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):

服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。

第三次握手(ACK=1,ACKnum=y+1)

客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1

发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。

三次握手的过程的示意图如下:


三次握手

TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。

第一次挥手(FIN=1,seq=x)

假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。

发送完毕后,客户端进入 FIN_WAIT_1 状态。

第二次挥手(ACK=1,ACKnum=x+1)

服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。

发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。

第三次挥手(FIN=1,seq=y)

服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。

发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。

第四次挥手(ACK=1,ACKnum=y+1)

客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。

服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。

客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。

四次挥手的示意图如下:


四次挥手

搞清楚握手和挥手规则,我们就可以设计如何防止或者说如何减少 SYN 攻击:

参考

https://www.jianshu.com/p/9968b16b607e(图片来源)
https://hit-alibaba.github.io/interview/basic/network/HTTP.html
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014320037274136d31bd9979d648cd822375394e29a871000

上一篇下一篇

猜你喜欢

热点阅读