网络编程总结

socket, socket套接字分类, 基于tcp的socket, 常见错误

复制代码

img

  ##为何须要sockethtml

复制代码

“”“
在标准的OIS模型中并无规定说必须有socket层,也就是说不使用socket也能完成通信,是的,的确如此!

那为何须要socket呢?一个字 懒,程序员都是懒的!

咱们发现尚未开始实现应用程序逻辑,就须要花大把时间来实现各类协议,太特么费事儿了,就有人专门把协议中一堆复杂的事情进行了封装,因而socket就诞生了!

有了socket之后,无需本身编写代码实现三次握手,四次挥手,ARP请求,打包数据等等,socket已经封装好了,只须要遵循socket的规定去编程,写出的程序天然就是遵循tcp/udp标准的。
”“”

复制代码

  ##scoket套接字分类python

复制代码

# 基于文件类型的套接字家族

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,能够经过访问同一个文件系统间接完成通讯

# 基于网络类型的套接字家族

套接字家族的名字:AF_INET

(还有AF_INET6被用于ipv6,还有一些其余的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是不多被使用,或者是根本没有实现,全部地址家族中,AF_INET是使用最普遍的一个,python支持不少种地址家族,可是因为大部通信都是网络通信,因此大部分时候使用AF_INET)

复制代码

  ##python中的socketmysql

复制代码

#需明确:关于网络协议 和socket相关概念,对于全部编程语言都是一致的,区别仅仅是各编程语言的函数名称不一样

# 1.导入socket模块
import socket
# 2.建立socket对象 函数定义以下
socket.socket(socket_family,socket_type,protocal=0)
    #socket_family 能够是 AF_UNIX 或 AF_INET。
    #socket_type 能够是 SOCK_STREAM表示TCP协议 或 SOCK_DGRAM表示UDP协议。
    #protocol 通常不填,默认值为 0。
    
# 2.1获取TCP 套接字
tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 或者 后面的参数都有默认值,能够不写,默认建立的是TCP协议socket
tcpSock = socket.socket()

# 2.2获取udp/ip套接字
udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

#因为 socket 模块中有太多的属性。可使用'from module import *'语句。使用 'from socket import *',把 socket 模块里的全部属性都导入当前命名空间里了,这样能大幅减短代码。
#例如:tcpSock = socket(AF_INET, SOCK_STREAM)


#要明确一点:不管是客户端服务器端都使用的都是socket对象
“”“
服务端套接字函数
s.bind()    绑定(主机,端口号)到套接字
s.listen()  开始TCP监听
s.accept()  被动接受TCP客户的链接,(阻塞式)等待链接的到来

客户端套接字函数
s.connect()     主动初始化TCP服务器链接
s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

公共用途的套接字函数
s.recv()            接收TCP数据
s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
s.recvfrom()        接收UDP数据
s.sendto()          发送UDP数据
s.getpeername()     链接到当前套接字的远端的地址
s.getsockname()     当前套接字的地址
s.getsockopt()      返回指定套接字的参数
s.setsockopt()      设置指定套接字的参数
s.close()           关闭套接字

面向锁的套接字方法
s.setblocking()     设置套接字的阻塞与非阻塞模式
s.settimeout()      设置阻塞套接字操做的超时时间
s.gettimeout()      获得阻塞套接字操做的超时时间
”“”

复制代码

  ##基于TCP的socketlinux

#前言:已经明确socket是别人封装好的接口,使用接口就变的简单了

按照通信流程编写代码便可

  ##TCP通信流程程序员

img

  ##socket运行示例web

复制代码

--------------------#socket服务器-------------------------------
import socket

# 做为服务器必明确本身的ip和端口号    而且不该该变化
# 参数1指定 socket类型AF_INET 表示网络类型
#参数2指定的传输协议为 SOCK_STREAM表示TCP协议 SOCK_DGRAM UDP协议
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)    # 1.买电话机

# 默认就是网络类型   TCP协议
#server = socket.socket()

# 127.0.0.1这叫回送地址   表示当前电脑自己
# ip必定本机ip   本机可能会有多个IP (无线|有限)
# 注意:须要参数是一个元组   端口就是普通整数
server.bind(("127.0.0.1",1688))   # 2.插入手机卡

# 不管是服务器端仍是客户端 都是socket 类型

# 开始监听1688端口    盯着这个端口看之后没有数据过来
server.listen()   # 3.手机开始待机

# 接收连接请求
# 第一个是表示客户端的socket    第二个客户端的地址信息
client,addr = server.accept()   # 4.接电话
# print(type(client))
# print(addr)


# 5.收发数据
data = client.recv(1024)
print(data)
client.send("copy!".encode("utf-8"))

server.close()  # 关机

----------------------#socket客户端-----------------------------------
import socket

# 买个电话
client = socket.socket()

# 做为客户端 ip和端口能够变化   全部系统会自动分配随机端给客户端
client.connect(("127.0.0.1",1688))

# 开始通话
# 发送数据   注意发送的内容只能是二进制  bytes
client.send("hello".encode("utf-8"))

# 接收数据  单位为字节
data = client.recv(1024)
print(data)

client.close()----------------------------------------------------------------------#总结注意TCP中必须先启动服务器再启动客户端,不然客户端因为没法连接服务器,直接报错!

复制代码

  ##改版之后的socketsql

复制代码

#注解:一个最基本的TCP通信,可是创建是为了传输数据,二传输数据不少时候并非一次性就传输完成了,须要屡次收发过程,因此须要给代码加上循环
————————————socket服务器.py————————————————————
import socket
server = socket.socket()
server.bind(("127.0.0.1",8989))
server.listen()

while True:
    client_socket,client_addr = server.accept()
    buffer_size = 1024 #缓冲区  就是一个临时的容器  
    # 缓冲区大小   不能随便写  太大会致使内存溢出    过小效率低    在内存可以承受的状况下 大一些

    while True:
        try:
            data = client_socket.recv(1024)
            # 在linux中 对方若是强行下线了  服务器不会抛出异常 只会收到空消息
            # 得加上判断 若是为空则意味着 对方下线了 应该关闭连接  跳出循环
            # windows上正常关闭 也会收到空消息   if 必需要加
            if not data:
                client_socket.close()
                break

            print(data.decode("utf-8"))  # 解码时必须保证双方赞成编码方式
            # 转为大写在发回去
            client_socket.send(data.upper())
        except ConnectionResetError as e:
            print("%s %s" % (client_addr[0],client_addr[1]),e)
            # 若是对方下线了  那服务器也应该关闭对应的客户端对象
            client_socket.close()
            break


# 一般服务器不会关闭
# server.close()
————————————socket客户端.py————————————————————
import socket

client = socket.socket()
# 指定服务器的端口和ip    客户端的端口系统自动分配的
client.connect(("127.0.0.1",8989)) #


# 添加循环 用来重复收发数据
while True:
    # 收发的顺序  必须很对面相反  不然卡死了
    msg = input("输入内容:")
    if not msg:continue
    client.send(msg.encode("utf-8"))
    print("sended!")
    data = client.recv(1024)
    print(data.decode("utf-8"))


client.close()

复制代码

  ##socket常见错误shell

复制代码

#一、端口占用 

在调试过程当中,可能会碰见如下错误:
问题发生缘由:

1》.多是因为你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用致使!

2》.三次握手或四次挥手时,发生了异常致使对方程序已经结束而服务器任然处于time_wait状态致使!

3》.在高并发的场景下,因为连接的客户端太多,也会产生大量处于time_wait状态的连接

解决的方案:

    第1种缘由,很简单关闭以前运行的服务器便可

    第2,3中缘由致使的问题,有两种解决方案:

1》.设置服务器重用端口
#加入一条socket配置,重用ip和端口
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,必须在bind前加
phone.bind(('127.0.0.1',8081))
2》.经过调整linux内核参数解决(了解)
发现系统存在大量TIME_WAIT状态的链接,经过调整linux内核参数解决,
vi /etc/sysctl.conf
编辑文件,加入如下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
而后执行 /sbin/sysctl -p 让参数生效。
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少许SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。容许将TIME-WAIT sockets从新用于新的TCP链接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP链接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

#示例
import socket
server = socket.socket()

server.bind(("127.0.0.1",9891))
server.listen()
server.accept()

server.close()


# 若是已经开启了服务器  再次运行将会抛出 端口占用异常  把以前开启的服务器关掉便可
"""
有些状况   明明已经关闭了进程 可是 仍是端口占用  
多是进程正在后台运行  能够经过端口查出进程  进行关闭
windows下
netstat -ano  | findstr 9898
tasklist | findstr 进程id    拿到进程名称
taskkill /f /t /im 进程名称

大招: 重启电脑
"""

复制代码

 ## 半连接数, 粘包问题,操做系统缓存理解, 粘包解决方案,strutc模块, 自定义报头 数据库

复制代码

2.强行关闭连接
当客服端与服务器连接成功后,若是一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会致使另外一方发送问题,因此要分别在服务端和客户端针对发送和接收代码添加try exception进行异常判断

#错误以下

img

复制代码

#1.socket
    套接字,本质上是一个模块,里面封装了一些网络通信协议
    是处于传输层和应用层之间的一个抽象层,实际在OSI中并不存在
    也就是没有socket也能可以通信 ,可是这样一来 咱们必须彻底按照OSI规定的各类协议来编码
    这是一个重复,复杂的过程,为了提升开发效率,就出现了socket模块,专门帮咱们封装了传输层如下的一堆协议
    有socket模块以后 当咱们须要编写网络通信的程序时,就不须要在关心具体的协议细节,直接使用socket提供的功能接口便可

“”“
门面设计模式,隐藏复杂的内部细节,仅提供最简单的使用接口 本质就是封装
”“”
#2.TCP通信
    网络通信必定分为两端,服务器和客户端
    服务器:
        1.建立socket对象   server = socket.socket()
        2.绑定一个固定的ip和端口  server.bind    ip必须是本机ip    端口1024-65535    不要使用常见的端口   web:80 / 8080  mysql 3306   ssh:22  ftp:21
        3.开始监听客户端的到来   server.listen
        4.接收客户端的连接请求   server.accept    # 阻塞直到客户连接到来  没有新链接则不可能执行该函数
        5.收发数据   须要循环
             recv 收    收多少字节数
             send 发    只能发二进制数据
    客户端:
        1.建立socket对象  client = socket.socket()
        2.连接服务器    client.connect((ip,port))
        3.收发数据   一般须要循环
            send 发    只能发二进制数据
            recv 收    收多少字节数
        4.断开连接   client.close()
#3.常见的异常
    1.一方异常下线,另外一方还要收发数据  ,ConnectionResetError
    在send  recv的地方 加上try   客户端也须要异常处理
    2.端口占用异常
    重复运行服务器
    以前的进程没有正确关闭
    关闭和打开端口 都是操做系统负责  在一些极端状况下   可能应用程序已经正确接收而且通知了操做系统要关闭端口
    可是操做系统 没及时处理
        2.1 更换端口
        2.2 查后台进程 杀掉它
        2.3 重启服务器电脑
#4循环通信

复制代码

  ##半链接数编程

复制代码

“”“
三次握手没有完成 称之为半链接  

缘由1   恶意客户端没有返回第三次握手信息

缘由2   服务器没空及时处理你的请求 

socket中  listen(半链接最大数量)
”“”

#理解
import socket

server = socket.socket()
server.bind(("127.0.0.1",1688))
server.listen(5)
# backlog  # 了解4
# 最大半链接数 本质就是一个数组  未完成连接的socket 就会被加入到数组中  ,
# 每一次执行accept 就会挑一个来完成三次握手  ,若是达到最大限制  额外的客户端将直接被拒绝
# 咱们能够调整内核参数来修改 最大等待时长   若是超时  客户仍是没有回复第三次握手信息 就直接删除

复制代码

img

  

  ##粘包问题(TCP)和操做系统缓存理解

复制代码

TCP流式协议, 指的是数据与数据之间没有明确的分界线,致使不能正确读取数据, 就像水  一杯水和一杯牛奶倒在一块儿了!

#要理解粘包问题,须要先了解TCP协议传输数据时的具体流程
“”“
应用程序没法直接操做硬件,应用程序想要发送数据则必须将数据交给操做系统,而操做系统须要须要同时为全部应用程序提供数据传输服务,也就意味着,操做系统不可能立马就能将应用程序的数据发送出去,就须要为应用程序提供一个缓冲区,用于临时存放数据,,具体流程以下:
##### 发送方:

当应用程序调用send函数时,应用程序会将数据从应用程序拷贝到操做系统缓存,再由操做系统从缓冲区读取数据并发送出去

##### 接收方:

对方计算机收到数据也是操做系统先收到,至于应用程序什么时候处理这些数据,操做系统并不清楚,因此一样须要将数据先存储到操做系统的缓冲区中,当应用程序调用recv时,其实是从操做系统缓冲区中将数据拷贝到应用程序的过程

上述过程对于TCP与UDP都是相同的不一样之处在于:

##### UDP:

UDP在收发数据时是基于数据包的,即一个包一个包的发送,包与包之间有着明确的分界,到达对方操做系统缓冲区后也是一个一个独立的数据包,接收方从操做系统缓冲区中将数据包拷贝到应用程序
这种方式存在的问题:

1.发送方发送的数据长度每一个操做系统会有不一样的限制,数据超过限制则没法发送

2.接收方接收数据时若是应用程序的提供的缓存容量小于数据包的长度将形成数据丢失,而缓冲区大小不可能无限大
##### TCP:

当咱们须要传输较大的数据,或须要保证数据完整性时,最简单的方式就是使用TCP协议了

与UDP不一样的是,TCP增长了一套校验规则来保证数据的完整性,会将超过TCP包最大长度的数据拆分为多个TCP包 并在传输数据时为每个TCP数据包指定一个顺序号,接收方在收到TCP数据包后按照顺序将数据包进行重组,重组后的数据全都是二进制数据,且每次收到的二进制数据之间没有明显的分界
”“”

######重点来了#######
“”“
粘包 仅发生在TCP协议中   

1. 发送端 发送的数据量小 而且间隔短 会粘
2. 接收端 一次性读取了两次数据的内容    会粘 
3. 接收端 没有接收完整  剩余的内容 和下次发送的粘在一块儿

不管是那种状况,其根本缘由在于  接收端不知道数据到底有多少 

解决方案就是 提早告知接收方 数据的长度  
”“”

复制代码

  ##粘包解决方案

复制代码

“”“
先发长度给对方  再发真实数据  



#发送端

1.使用struct 将真实数据的长度转为固定的字节数据

2.发送长度数据 

3.发送真实数据  

接收端

1.先收长度数据    字节数固定  

2.再收真实数据      真实可能很长 须要循环接收 

发送端和接收端必须都处理粘包 才算真正的解决了 

案例: 远程CMD程序 “”“需求: 基于TCP开发一款远程CMD程序  客户端链接服务器后 能够向服务器发送命令  服务器收到命令后执行 不管执行成功仍是失败 将执行结果返回给客户端  执行系统指令使用subprocess模块完成”“”
”“”

#案例 远程CMD程序 
————————————服务器.py————————————————————
import socket
import subprocess
import struct
from 二_CMD程序 import  smallTool

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",1688))
server.listen()
# back

while True:
    # socket,addr一个元组 客户端的ip和port
    client,addr = server.accept()
    print("客户端连接成功!")
    # 循环收发数据
    while True:
        try:
            cmd = smallTool.recv_data(client)
            if not cmd:
                break
            print(cmd)

            p = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            # 不要先读err错误信息  它会卡主  缘由不详  linux不会有问题  tasklist  netstat - ano啥的
            data = p.stdout.read()
            err_data = p.stderr.read()

            len_size = len(data) + len(err_data)
            print("服务器返回了: %s " %  len_size)

            len_bytes = struct.pack("q",len_size)

            # 在发送真实数据前先发送 长度
            client.send(len_bytes)

            # 返回的结果恰好就是二进制
            # 发送真实数据
            client.send(data + err_data)


        except ConnectionResetError as e:
            print("客户端了挂了!",e)
            break
    client.close()

#server.close()
————————————客户端.py————————————————————
"""
客户端输入指令
服务器接收指令并执行  最后返回执行结果
"""

import socket
from 二_CMD程序 import smallTool
import struct

client = socket.socket()
try:
    client.connect(("127.0.0.1",1688))
    print("连接成功!")
    while True:
        msg = input("请输入要执行指令:").strip()
        if msg == "q": break
        if not msg: continue
        # 发送指令
        # 先发长度
        len_bytes = struct.pack("q",len(msg.encode("utf-8")))
        client.send(len_bytes)
        # 在发指令
        client.send(msg.encode("utf-8"))

        data = smallTool.recv_data(client)
        print(data.decode("GBK"))

    client.close()
except ConnectionRefusedError as e:
    print("连接服务器失败了!",e)
except ConnectionResetError as e:
    print("服务器挂了!", e)
    client.close()
————————————smalltool.py——————————————————
“”“
说明:该模块做用于服务器和客户端读长度,读数据内容的公共代码,抽调出来的模块
返回的是:data 为二进制数据
”“”
import struct

def recv_data(client):
    # 接收长度数据  固定位8个字节
    len_bytes = client.recv(8)
    # 转换为整型
    len_size = struct.unpack("q", len_bytes)[0]
    print("服务器返回了%s长度的数据" % len_size)

    # 再接收真实数据
    # 问题  若是数据量太大 则不能 一次收完  必须循环一次收一部分

    # 缓冲区大小
    buffer_size = 1024
    # 已接受大小
    recv_size = 0

    # 最终的数据
    data = b""
    while True:
        # 若是剩余数据长度 大于缓存区大小 则缓冲区有多大就读多大
        if len_size - recv_size >= buffer_size:
            temp = client.recv(buffer_size)
        else:
            #  剩余数据长度 小于缓冲区大小   剩多少就收多少
            temp = client.recv(len_size - recv_size)
        recv_size += len(temp)
        data += temp
        # 当已接受大小等于数据总大小则跳出循环
        if recv_size == len_size:
            break

    # print(data.decode("gbk"))  # windows执行指令返回的结果默认为GBK
    return data

复制代码

  ## struct模块

复制代码

# struct 是一个内置模块  做用是 将python中的类型 转为字节数据 而且长度固定

import struct

# 将整数 转为固定长度的二进制
data = struct.pack("q",100000000000000000)
print(data)
print(len(data))

# 将二进制转换回整型  返回一个元组
res = struct.unpack("q",data)[0]
print(res)

复制代码

  ##引出自定义报头以前文件下载案例

复制代码

-----------------------------服务器.py-------------------------------------------------
"""
#理解注释
粘包问题解决了,可是存在一个问题
当文件过大时,不能直接发给send发给操做系统
"""
import socket
import os
import struct
"""
发文件大小-----发文件长度-----发数据
"""
server =socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
while True:
    client,addr = server.accept()
    print("客户端链接成功")
    f=None
    try:
        #先发送文件大小
        path = r"D:\python8期\课堂内容\day32\视频\2.半连接数.mp4"
        file_size=os.path.getsize(path)
        print(file_size)
        len_bytes=struct.pack("q",file_size)
        #发送长度
        client.send(len_bytes)
        #循环发送文件数据,每次发送2048
        f=open(path,'rb')
        while True:
            temp = f.read(2048)
            if not temp:
                break
            client.send(temp)
        print("文件发送完毕")
    except Exception as e:
        print("出问题了",e)
    finally:
        if f:
            f.close()
    client.close()
-----------------------------客户端.py-------------------------------------------------
import socket
import struct
client=socket.socket()
try:
    client.connect(("127.0.0.1",1688))
    print("链接成功")

    #第一步 先收长度
    file_size=struct.unpack("q",client.recv(8))[0]
    #第二步 再收文件内容
    #已收大小
    recv_size=0
    #缓冲区
    buffer_size=2048
    f=open("new.mp4",'wb')

    while True:
        if file_size - recv_size >= buffer_size:
            temp=client.recv(buffer_size)
        else:
            temp=client.recv(file_size - recv_size)
        f.write(temp)
        recv_size += len(temp)
        print("已接收大小:%s%%"%(recv_size/file_size *100))
        if recv_size == file_size:
            break
    f.close()
except ConnectionRefusedError as e:
    print("服务器链接失败")
    client.close()

复制代码

  ##自定义报头

复制代码

“”“
当须要在传输数据时 传呼一些额外参数时就须要自定义报头 

报头本质是一个json  数据   ,例如咱们要实现文件上传下载,不光要传输文件数据,还须要传输文件名字,md5值等等,如何能实现呢?

具体过程以下: 

发送端

    1 发送报头长度 ,定义的报头包含所需的信息

    2  发送报头数据    其中包含了文件长度  和其余任意的额外信息  

    3  发送文件内容

   

接收端 

    1.接收报头长度 

    2.接收报头信息

    3.接收文件内容
”“”

#案例:优化上一个下载视频文件案例
——————————————服务器.py——————————————————
import socket
import os
import struct
import json
"""
发文件大小-----发文件长度-----发数据
"""
server =socket.socket()
server.bind(("127.0.0.1",1688))
server.listen()
while True:
    client,addr = server.accept()
    print("客户端链接成功")
    f=None
    try:

        path = r"D:\python8期\课堂内容\day32\视频\2.半连接数.mp4"
        file_size = os.path.getsize(path)

        file_info={"file_name":"广东雨神.mp4","file_size":file_size}
        json_str=json.dumps(file_info).encode("utf-8")
        #发送报头长度
        client.send(struct.pack("q",len(json_str)))
        #发送报头
        client.send(json_str)
        #发文件
        #循环发送文件数据,每次发送2048
        f=open(path,'rb')
        while True:
            temp = f.read(2048)
            if not temp:
                break
            client.send(temp)
        print("文件发送完毕")
    except Exception as e:
        print("出问题了",e)
    finally:
        if f:
            f.close()
    client.close()
——————————————客户端.py——————————————————
import socket
import struct
import json
client=socket.socket()
try:
    client.connect(("127.0.0.1",1688))
    print("链接成功")

    #第一步 先收报头长度
    head_size=struct.unpack("q",client.recv(8))[0]
    #第二步 再收报头信息
    head_str = client.recv(head_size).decode("utf-8")
    file_info=json.loads(head_str)
    print("报头数据:",file_info)
    file_size=file_info.get("file_size")
    file_name=file_info.get("file_name")
    #已收大小
    recv_size=0
    #缓冲区
    buffer_size=2048
    f=open(file_name,'wb')

    while True:
        if file_size - recv_size >= buffer_size:
            temp=client.recv(buffer_size)
        else:
            temp=client.recv(file_size - recv_size)
        f.write(temp)
        recv_size += len(temp)
        print("已接收大小:%s%%"%(recv_size/file_size *100))
        if recv_size == file_size:
            break
    f.close()
except ConnectionRefusedError as e:
    print("服务器链接失败")
    client.close()

复制代码

UDP协议, DNS服务器, 进程, 多道技术

1.  半链接数
    指的是没有完成的连接
    1.1 客户端恶意攻击,只进行两次握手 致使服务器处于等待状态
    1.2 有大量的客户端同时访问  (高并发) 服务器处理不过来
    listen() 函数中能够指定最大的半链接数量  超出这个数量将会被拒绝
    处于半链接状态的连接  会在超时后被丢弃
2.  粘包问题 网络编程重点
    粘包问题只出如今TCP协议中,由于TCP是流式协议,数据之间没有明显分隔
    之因此出现粘包
    1. 把时间间隔短的 数据量小的数据 一次性发送
    2. 数据已经到达 接收多了或少了 都会粘包
    之因此粘包本质缘由就是接收方不清楚数据长度
    解决方案就是提早告诉接收方数据长度

    发送方
    1.发送数据长度
        长度信息也会粘包  因此 必定要确保每一次发送的数据信息的长度 所占的字节是固定的
        例如  8  或 4
        struct  模块就能够把一个python的数据类型 转为二进制 而且字节数固定
        q 转为8字节    i   转为4字节

    2.发送真实数据

    接收方
    1.先接受长度
    2.再接收真实数据

3. 自定义报头
    不只要传输数据过去  可能还要额外传输一些信息  例如文件名称  大小 md5等等........
    报头就是传输数据前 先传输的一个信息   一般用json
    本质就是  多传输了一个json数据
    发送
    1.先发json数据的长度
    2.在发送json(报头)数据
    3.在发送真实数  也会粘包 因此要把长度放入报头中

                    json也能够传输二进制   可是json本质是字符串 因此必须把二进制转为字符串  接收方吧字符串转二进制
                    BASE64 编码
    接收:
    1.先收报头长度
    2.收报头
    3.真实数据

复制代码

  ##UDP协议

复制代码

#什么是UDP协议
    用户数据包协议
    OSI模型中 属于传输层的协议, 仅用于不要求可靠性,不要求分组顺序且数据量较小的简单传输,力求快

#如何使用
    通信流程相似对讲机    只管发送无论对方是否接受到 甚至不关心对方在不在
    1.买对讲机
    2.固定频道
    3.收发数据

    1.买个对讲机
    2.指定发送的频道
    3.收发数据

与TCP的区别 *****
不可靠传输
不须要创建链接
不会粘包
单次数据包不能太大

 

代码 :
服务器端
服务器不须要监听 listen
不须要接收请求 accept
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)

客户端:
不须要创建链接
收数据 recvfrom(缓冲区大小)
发数据 sendto(数据,地址)

#示例1
—————————————服务器.py——————————————————————
from socket import *

# 建立基于UDP的scoket  必须手动指定
server = socket(AF_INET,SOCK_DGRAM)

server.bind(("127.0.0.1",1688))

while True:
    data,addr = server.recvfrom(1024)
    print("收到来自%s的消息 : %s"  % (addr,data))
    server.sendto(data.upper(),addr)

# server.close()
—————————————客户端.py——————————————————————
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#  UDP 不须要创建连接

# 发送数据时  要指定接受方地址
client.sendto("hello".encode("utf-8"),("127.0.0.1",1688))

data,addr = client.recvfrom(1024)
print("收到来自%s的消息 : %s"  % (addr,data))


client.close()


#示例2:在接受的时候 缓冲区大小必须大于数据的长度
—————————————服务器.py——————————————————————
from socket import *

# 建立基于UDP的scoket  必须手动指定
server = socket(AF_INET,SOCK_DGRAM)

server.bind(("127.0.0.1",1688))


# 在接受的时候 缓冲区大小必须大于数据的长度
data,addr = server.recvfrom(1)

print(data)
—————————————客户端.py——————————————————————
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#  UDP 不须要创建连接

# 发送数据时  要指定接受方地址
client.sendto("hello".encode("utf-8"),("127.0.0.1",1688))

# client.sendto("world".encode("utf-8"),("127.0.0.1",1688))

client.close()

#示例3:UDP发不了大数据
—————————————服务器.py——————————————————————
from socket import  *

server = socket(AF_INET,SOCK_DGRAM)

server.bind(("127.0.0.1",1688))

while True:
    data,addr = server.recvfrom(1024*1024)
    print(data)

—————————————客户端.py——————————————————————
"""UDP单次数据包不能太大"""

from socket import  *

client = socket(AF_INET,SOCK_DGRAM)

data = b""
for i in range(1024 * 60):
    data += b"1"

client.sendto(data,("127.0.0.1",1688))

#示例4:多个客户端同时处理
—————————————服务器.py——————————————————————
from socket import  *
import time

server = socket(AF_INET,SOCK_DGRAM)
server.bind(("192.168.13.93",1688))

# UDP 能够处理多个客户端 可是并非真正的同时处理 而是按顺序处理 速度很是快  感受像是同时处理  叫并发
# 并行 真正同时处理     想要真正的同时运行  必须由多个执行单位


# 模拟一下聊天室
# 客户端发消息来  服务器就把客户端地址存起来
# 再有消息来 服务器循环给每个人都发一份

# 客户端列表
clients = {}


while True:
    try:
        data,addr = server.recvfrom(1472)
        if addr[0] not in clients:
            clients[addr[0]] = addr
        try:
            print("%s说:%s" % (addr[0],data.decode("utf-8")))
        except:
            print("编码有问题啊 .....")
        # 遍历全部客户端  转发消息给他们
        for k,v in clients.items():
            server.sendto(data,v)
    except Exception as e:
        # print(e)
        pass

    # 若是要限制发消息不能太频繁 思路以下:
    # 收到数据后 把你的地址 和发送数据的时间记录下来
    # 遍历出全部数据 1888888888  188888888.1   10
    # 以当前时间为起始  100   取出消息的时间  若是时间范围为98-100

—————————————客户端.py——————————————————————
“”“
多个客户端问题:能够在一个客户端.py模块执行多个窗口来测试
pycharm默认只能开一个问题:界面code下面,有个start标记----选择里面的Edit Configurations---------而后在弹出的窗口右上方打勾---就能够执行多个客户端
”“”
from socket import  *

client = socket(AF_INET,SOCK_DGRAM)
while True:
    # msg = input("msg:").strip()
    client.sendto("client2".encode("utf-8"),("127.0.0.1",1688))
    data,addr = client.recvfrom(1472)
    print(data)

复制代码

  ##DNS服务器

复制代码

DNS Domain Name System 全称 :域名解析服务器
    DNS 是干什么的 :
        将域名转换为IP地址     要链接服务器 必定的知道IP  \
    为何须要DNS
        单独ip不方便记忆  因此咱们吧ip和一个域名绑定到一块儿   域名一串有规律的字符串  www.baidu.com

    DNS 是CS结构的server端
    DNS 使用的是UDP 协议 由于  传输的数据小 但对速度要求高  一个DNS要服务不少计算机

    http://     news.cctv.com    /2019/05/29/ARTIXRqlqFBp59eECweiXTUU190529.shtml
    协议名称        域名                      文件路径

    DNS 本质就是一个数据库  里面就存储 域名和ip的对应关系
    news.cctv.com
    .com  顶级域名
    cctv  二级域名
    news  三级域名

复制代码

  ##进程

复制代码

#进程是什么?
    正在运行的程序
进程来自于操做系统  没有操做系统就没有进程

操做系统是什么?
    也是一套软件  ,

    主要功能
        1.控制硬件,隐藏丑陋复杂的硬件细节
        2.将无序的硬件竞争变得有序

早些年 计算机同一时间只能运行一个程序,这时候是不可能并发的
要并发 固然须要不一样的应用程序   ,如何使多个应用程序同时被运行
这就须要多道技术来支持

复制代码

  ##多道技术

复制代码

#多道技术:为了提升计算机的利用率
1.空间复用  把内存分割为不一样区域 ,每一个区域装入不一样的程序
2.时间复用  当一个程序执行IO操做时,切换到另外一个程序来执行
    光切换还不行 必须在切换前保存当前的状态    以便与恢复执行

当内存中有多个程序时,必须保证数据是安全的
每一个进程之间的内存区域是相互隔离的,并且是物理层面的隔离

有了多道技术
    计算机就能够在同一时间处理多个任务(看着像 因为计算远比人块 因此感受同时执行了)

注意:并非多道就必定提升了效率
    若是多个任务都是纯计算 那么切换反而下降了效率
    遇到IO操做才应该切换    这才能提升效率
相关文章
相关标签/搜索