Python之TCP详解和 OSI七层模型

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lianjiaokeji/article/details/83042467

1.OSI七层模型和TCP/IP四层

基本模型:

在这里插入图片描述

OSI七层模型

先有模型,后有协议,先有标准,后有实践,TCP/IP反之
ARP协议,获取主机的mac地址,全世界唯一
应用程序:QQ、微信,我们开发都是在传输层
七层模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层

在这里插入图片描述

TCP/IP四层

四层:应用层、传输层、网络层、数据链路层
TCP可靠传输:三次握手,四次挥手
建立连接,三次握手
在这里插入图片描述
数据传输
在这里插入图片描述

断开连接,四次挥手
在这里插入图片描述

2.套接字

套接字的基本概念

在这里插入图片描述

三种套接字(监听套接字、客户端套接字、对等连接套接字)

在这里插入图片描述

创建套接字

服务器端:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/10/13 7:38
# @Author  : DoubleChina
# @Site    :
# @File    : SocketTest.py
# @Software: PyCharm
import socket

# 创建套接字
sock = socket.socket()
# 把监听套接字,绑定到本地的9999端口上面
sock.bind(('', 9999))
# 开始监听,等待套接字连接,
# 默认写5就好,在没有accept之前,能挂起最大连接数
# python3.6版本后才起效果
sock.listen(5)
# socket.socket fd=3,  文件描述符(唯一标识了一个socket)
# family=AddressFamily.AF_INET,(#AF_INET表示IPV4)
# type=SocketKind.SOCK_STREAM,(#SOCK_STREAM表示TCP)
# proto=0, #通常都是0
# laddr=('0.0.0.0', 0)
print(sock)
while True:
    # 线程阻塞
    print('连接已经建立,等待接受数据')
    conn, addr = sock.accept()
    while True:
        # recv也会进行阻塞
        data = conn.recv(1024)
        if data:
            print('接受客户端的消息:', data)
            conn.send(data)
        else:
            break

# sock.close()


客户端

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/10/13 7:38
# @Author  : DoubleChina
# @Site    :
# @File    : SocketTest.py
# @Software: PyCharm
import socket

# 创建套接字
s = socket.socket()
# 连接套接字,ip和端口必须是服务器上的
s.connect(('127.0.0.1', 9999))
while True:
    data = input("输入发送消息:")
    if 'q' == data:
        s.close()
        break
    s.send(data.encode())
    print('接受服务器返回的消息:', s.recv(1024))
# s.close()

普通套接字实现的服务端的缺陷

一次只能服务一个客户端
在这里插入图片描述
accept 阻塞
在没有新的套接字来之前,不能处理已经建立连接的套接字的请求。
recv 阻塞
在没有接受到客户端请求数据之前,不能与其他客户端建立连接!
普通服务器的IO模型
在这里插入图片描述
###非阻塞套接字
非阻塞套接字服务器端

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/10/13 7:38
# @Author  : DoubleChina
# @Site    :
# @File    : SocketTest.py
# @Software: PyCharm
import socket

# 创建套接字
sock = socket.socket()
# 把套接字设置未非阻塞,这必须要在其他操作之前
sock.setblocking(False)
# 把监听套接字,绑定到本地的9999端口上面
sock.bind(('', 9999))
# 开始监听,等待套接字连接,
# 默认写5就好,在没有accept之前,能挂起最大连接数
# python3.6版本后才起效果
sock.listen(5)
# socket.socket fd=3,  文件描述符(唯一标识了一个socket)
# family=AddressFamily.AF_INET,(#AF_INET表示IPV4)
# type=SocketKind.SOCK_STREAM,(#SOCK_STREAM表示TCP)
# proto=0, #通常都是0
# laddr=('0.0.0.0', 0)
print(sock)
print('开始监听')
client_list = []
while True:  # 通过while循环不断的检测,有没有资源到达
    try:
        # print('连接已经建立,等待接受数据')
        # 线程阻塞
        conn, addr = sock.accept()
    except BlockingIOError as e:
        pass
    else:
    #1.sock.setblocking(False)无法生效,非阻塞不起效果?
     #这里也要设置False
        conn.setblocking(False)
        print('客户端{},连接成功'.format(addr))
        # 客户端连接添加进来
        client_list.append(conn)
    # 遍历所有的socket进行遍历接受数据
    for client_socket in client_list:
        # data = client_socket.recv(1024)
        try:
            data = client_socket.recv(1024)
        except BlockingIOError as e:
            pass
        else:
            if len(data) > 0:
                print(data)
            else:
                client_socket.close()



# sock.close()

非阻塞套接字客户端

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2018/10/13 7:38
# @Author  : DoubleChina
# @Site    :
# @File    : SocketTest.py
# @Software: PyCharm
import socket

# 创建套接字
s = socket.socket()
# 连接套接字,ip和端口必须是服务器上的
s.connect(('127.0.0.1', 9999))
while True:
    data = input("输入发送消息:")
    if 'q' == data:
        s.close()
        break
    s.send(data.encode())
    # print('接受服务器返回的消息:', s.recv(1024))
# s.close()

socket connect操作一定会引发BlockingIOError异常,需要进行try except
如果连接没有建立,那么send操作引发OSError异常

非阻塞IO模型
在这里插入图片描述
非阻塞套接字服务器优化之IO多路复用epoll
IO多路复用epoll,其实就是把socket交给操作系统去监控
IO多路复用epoll实现逻辑就是事件循环加回调
在这里插入图片描述
epoll是惰性事件回调,惰性事件回调是由用户进程自己调用的,操作系统只起到通知的作用。
epoll是目前Linux上效率最高的IO多路复用 技术 !

在这里插入图片描述

epoll注册事件使用

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018/10/13 14:41
# @Author : DoubleChina
# @Site : 
# @File : EpollServer.py
# @Software: PyCharm
import socket
import selectors
import time

# 选择epoll
# window使用
# sel = selectors.DefaultSelector()
# linux使用
# 实例化一个epoll选择器
sel = selectors.EpollSelector()
servers = socket.socket()
servers.bind(('', 9999))
servers.listen(5)

print('开始监听')


# 注册事件
# 当客户端连接了,怎么办?
# 当客户端发送消息,应该怎么办?
def readable(conn):
    data = conn.recv(1024)
    if data:
        print(data)
    else:
        print('close', conn)
        # 取消注册事件
        sel.unregister(conn)
        conn.close()


def acc(server):
    conn, addr = server.accept()
    print('客户端{},连接成功'.format(addr))
    sel.register(conn, selectors.EVENT_READ, readable)


# 注册事件(套接字,事件,回调函数)
sel.register(servers, selectors.EVENT_READ, acc)

while True:
    # 返回有变化的套接字,是一个二元组的列表
    events = sel.select()
    # events事件信息
    # SelectorKey(
    # fileobj=<socket.socket fd=4, 生成了个打包对象,fileobj是对应的套接字
    # family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0,
    # laddr=('0.0.0.0', 9999)>,
    # fd=4,
    # events=1, 事件(1表示可读事件EVENT_READ)
    # data=<function acc at 0xb72475cc>) 对应的回调函数
    for key, mask in events:
        # print(key, mask)
        # 注册的回调函数
        callback = key.data
        # 惰性:此处还是需要我们自己去调用回调函数
        callback(key.fileobj)
        time.sleep(5)

猜你喜欢

转载自blog.csdn.net/lianjiaokeji/article/details/83042467