码迷,mamicode.com
首页 > 其他好文 > 详细

网络编程

时间:2019-05-09 20:12:34      阅读:128      评论:0      收藏:0      [点我收藏+]

标签:upper   文件的   输入   osi七层模型   func   middle   client   基础   全双工   

网络编程

一、网络基础

1. 软件开发的架构

1-1. C/S架构

C/S即:Client与Server ,中文意思:客户端与服务器端架构,这种架构也是从用户层面(也可以是物理层面)来划分的。

这里的客户端一般泛指客户端应用程序EXE,程序需要先安装后,才能运行在用户的电脑上,对用户的电脑操作系统环境依赖较大。

技术图片

1-2. B/S架构

B/S即:Browser与Server,中文意思:浏览器端与服务器端架构,这种架构是从用户层面来划分的。

Browser浏览器,其实也是一种Client客户端,只是这个客户端不需要大家去安装什么应用程序,只需在浏览器上通过HTTP请求服务器端相关的资源(网页资源),客户端Browser浏览器就能进行增删改查。

技术图片

B/S是特殊的C/S

2. 物理设备

网卡 :是一个实际存在在计算机中的硬件,每一块网卡上都有一个全球唯一的mac地址

交换机 :是连接多台机器并帮助通讯的物理设备,只认识mac地址

arp协议(地址解析协议) :
# 通过ip地址获取它的mac地址
# 由交换机完成的
# 广播 单播

路由器:完成局域网与局域网之间的联系

ip地址

# ipv4协议 4位的点分十进制  32位2进制表示
# 0.0.0.0 - 255.255.255.255

# ipv6协议 6位的冒分十六进制 128位2进制表示
# 0:0:0:0:0:0-FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
# 公网ip
每一个ip地址要想被所有人访问到,那么这个ip地址必须是你申请的

# 内网ip
    # 192.168.0.0 - 192.168.255.255
    # 172.16.0.0 - 172.31.255.255
    # 10.0.0.0 - 10.255.255.255

# 网关ip 一个局域网的网络出口,访问局域网之外的区域都需要经过路由器和网关
# 网段 指的是一个地址段 x.x.x.0  x.x.0.0  x.0.0.0
# ip地址
# 子网掩码 判断两台机器是否在同一个网段内的

# 255.255.255.0 子网掩码

端口:定位一台机器上的一个服务

# 0-65535
ip + port 确认一台机器上的一个应用

3. 协议

3-1. TCP协议

TCP(传输控制协议),面向连接、传输可靠(保证数据正确性,保证数据顺序)、用于传输大量数据(流模式)、速度慢,建立连接需要开销较多(时间,系统资源)。TCP支持的应用协议:Telnet(远程登录)、FTP(文件传输协议)、SMTP(简单邮件传输协议)。TCP用于传输数据量大,可靠性要求高的应用。

技术图片

可靠 慢 全双工通信
    # 建立连接的时候 : 三次握手
    # 断开连接的时候 : 四次挥手
    # 在建立起连接之后
        # 发送的每一条信息都有回执
        # 为了保证数据的完整性,还有重传机制
    # 长连接 :会一直占用双方的端口
    # IO(input,output)操作,输入和输出是相对内存来说的
        # write send  -  output
        # read recv   -  input
    # 能够传递的数据长度几乎没有限制
 三次握手
    # accept接受过程中等待客户端的连接
    # connect客户端发起一个syn链接请求
        # 如果得到了server端响应ack的同时还会再收到一个由server端发来的syc链接请求
        # client端进行回复ack之后,就建立起了一个tcp协议的链接
    # 三次握手的过程再代码中是由accept和connect共同完成的,具体的细节再socket中没有体现出来

# 四次挥手
    # server和client端对应的在代码中都有close方法
    # 每一端发起的close操作都是一次fin的断开请求,得到‘断开确认ack‘之后,就可以结束一端的数据发送
    # 如果两端都发起close,那么就是两次请求和两次回复,一共是四次操作
    # 可以结束两端的数据发送,表示链接断开了

3-2. UDP协议

UDP(用户数据报协议,User Data Protocol)

  • 面向非连接的(正式通信前不必与对方建立连接,不管对方状态就直接发送,像短信,QQ),不能提供可靠性、流控、差错恢复功能。UDP用于一次只传送少量数据,可靠性要求低、传输经济等应用。
  • UDP支持的应用协议:NFS(网络文件系统)、SNMP(简单网络管理系统)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。
无连接的 速度快
可能会丢消息
能够传递的数据的长度是有限的,是根据数据传递设备的设置有关系
tcp协议和udp协议的特点:
tcp是一个面向连接的,流式的,可靠的,慢的,全双工通信
        # 邮件 文件 http web
udp是一个面向数据报的,无连接的,不可靠,快的,能完成一对一、一对多、多对一、多对多的高效通讯协议
        # 即时聊天工具 视频的在线观看

4. osi七层模型

人们按照分工不同把互联网协议从逻辑上划分了层级:

技术图片

技术图片

二、套接字(socket)

工作在应用层和传输层之间的抽象层,帮助我们完成了所有信息的组织和拼接

1. 基于TCP协议的socket

###server端

import socket
sk = socket.socket()
sk.bind((‘127.0.0.1‘,8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b‘hi‘)        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)

1-1. client端

import socket
sk = socket.socket()           # 创建客户套接字
sk.connect((‘127.0.0.1‘,8898))    # 尝试连接服务器
sk.send(b‘hello!‘)
ret = sk.recv(1024)         # 对话(发送/接收)
print(ret)
sk.close()            # 关闭客户套接字

2. 基于UDP协议的socket

udp是无链接的,启动服务之后可以直接接受消息,不需要提前建立链接

2-1. server端

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind((‘127.0.0.1‘,9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b‘hi‘,addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字

2-2. client端

import socket
ip_port=(‘127.0.0.1‘,9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b‘hello‘,ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode(‘utf-8‘),addr)

3. 会发生黏包的两种情况

情况一 发送方的缓存机制
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

情况二 接收方的缓存机制
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

什么是粘包现象
   # 发生在发送端的粘包
       # 由于两个数据的发送时间间隔短+数据的长度小
       # 所以由tcp协议的优化机制将两条信息作为一条信息发送出去了
       # 为了减少tcp协议中的“确认收到”的网络延迟时间
   # 发生再接收端的粘包
       # 由于tcp协议中所传输的数据无边界,所以来不及接收的多条
       # 数据会在接收放的内核的缓存端黏在一起
   # 本质: 接收信息的边界不清晰
   ```

总结
黏包现象只发生在tcp协议中:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes
```python
import struct

ret = struct.pack(‘i‘,100000) #字节类型
print(ret)    #b‘\xa0\x86\x01\x00‘
rst=struct.unpack(ret)   #返回元组
print(rst) #(100000,)

解决方法

# 解决粘包问题
    # 自定义协议1
        # 首先发送报头
            # 报头长度4个字节
            # 内容是 即将发送的报文的字节长度
            # struct模块
                # pack 能够把所有的数字都固定的转换成4字节
        # 再发送报文
    # 自定义协议2
        # 我们专门用来做文件发送的协议
            # 先发送报头字典的字节长度
            # 再发送字典(字典中包含文件的名字、大小。。。)
            # 再发送文件的内容
import struct
import socket

sk = socket.socket()
sk.connect((‘127.0.0.1‘,9000))

msg = b‘hello‘
byte_len = struct.pack(‘i‘,len(msg))
sk.send(byte_len)   #  1829137
sk.send(msg)        #  1829139
msg = b‘world‘
byte_len = struct.pack(‘i‘,len(msg))
sk.send(byte_len)
sk.send(msg)

sk.close()
import time
import struct
import socket

sk = socket.socket()
sk.bind((‘127.0.0.1‘,9000))
sk.listen()

conn,_ = sk.accept()
byte_len = conn.recv(4)
size = struct.unpack(‘i‘,byte_len)[0]
msg1 = conn.recv(size)
print(msg1)
byte_len = conn.recv(4)
size = struct.unpack(‘i‘,byte_len)[0]
msg2 = conn.recv(size)
print(msg2)
conn.close()
sk.close()

4. 非阻塞io模型

技术图片

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 阻塞io模型
# 非阻塞io模型
# 事件驱动io
# io多路复用
# 异步io模型

import socket
sk = socket.socket()
sk.bind((‘127.0.0.1‘,9000))
sk.setblocking(False)
sk.listen()

conn_l = []
del_l = []
while True:
    try:
        conn,addr = sk.accept()   # 阻塞,直到有一个客户端来连我
        print(conn)
        conn_l.append(conn)
    except BlockingIOError:
        for c in conn_l:
            try:
                msg = c.recv(1024).decode(‘utf-8‘)
                if not msg:
                    del_l.append(c)
                    continue
                print(‘-->‘,[msg])
                c.send(msg.upper().encode(‘utf-8‘))
            except BlockingIOError:pass
        for c in del_l:
            conn_l.remove(c)
        del_l.clear()
sk.close()

# socket的非阻塞io模型 + io多路复用实现的
    # 虽然非阻塞,提高了CPU的利用率,但是耗费CPU做了很多无用功
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import time
import socket
sk = socket.socket()
sk.connect((‘127.0.0.1‘,9000))
for i in range(30):
    sk.send(b‘wusir‘)
    msg = sk.recv(1024)
    print(msg)
    time.sleep(0.2)
sk.close()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import time
sk = socket.socket()
sk.connect((‘127.0.0.1‘,9000))
while True:
    sk.send(b‘wusir‘)
    msg = sk.recv(1024)
    print(msg)
    time.sleep(0.2)
sk.close()

5. socketserver模块

直接实现tcp协议可并发的server端
技术图片

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import time
sk = socket.socket()
sk.connect((‘127.0.0.1‘,9000))
for i in range(3):
    sk.send(b‘hello,yuan‘)
    msg = sk.recv(1024)
    print(msg)
    time.sleep(1)

sk.close()
import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):  # 自动触发了handle方法,并且self.request == conn
        msg = self.request.recv(1024).decode(‘utf-8‘)
        self.request.send(‘1‘.encode(‘utf-8‘))
        msg = self.request.recv(1024).decode(‘utf-8‘)
        self.request.send(‘2‘.encode(‘utf-8‘))
        msg = self.request.recv(1024).decode(‘utf-8‘)
        self.request.send(‘3‘.encode(‘utf-8‘))

server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,9000),Myserver)
server.serve_forever()

6. 验证客户端的合法性

技术图片

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import time
import hmac

def get_md5(secret_key,randseq):
    h = hmac.new(secret_key,randseq)
    res = h.digest()
    return res
def chat(sk):
    while True:
        sk.send(b‘hello‘)
        msg = sk.recv(1024).decode(‘utf-8‘)
        print(msg)
        time.sleep(0.5)

sk = socket.socket()
sk.connect((‘127.0.0.1‘,9000))

secret_key = b‘alexsb‘
randseq = sk.recv(32)
hmaccode = get_md5(secret_key,randseq)

sk.send(hmaccode)
chat(sk)

sk.close()
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import hmac
import socket

def get_md5(secret_key,randseq):
    h = hmac.new(secret_key,randseq)
    res = h.digest()
    return res

def chat(conn):
    while True:
        msg = conn.recv(1024).decode(‘utf-8‘)
        print(msg)
        conn.send(msg.upper().encode(‘utf-8‘))

sk = socket.socket()
sk.bind((‘127.0.0.1‘,9000))
sk.listen()

secret_key = b‘alexsb‘
while True:
    conn,addr = sk.accept()
    randseq = os.urandom(32)
    conn.send(randseq)
    hmaccode = get_md5(secret_key,randseq)
    ret = conn.recv(16)
    print(ret)
    if ret == hmaccode:
        print(‘是合法的客户端‘)
        chat(conn)
    else:
        print(‘不是合法的客户端‘)
        conn.close()
sk.close()

网络编程

标签:upper   文件的   输入   osi七层模型   func   middle   client   基础   全双工   

原文地址:https://www.cnblogs.com/yinhaiping/p/10840381.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!