python进阶之 网络编程

otherhtml

互联网协议按照功能不一样分为osi七层或tcp/ip五层或tcp/ip四层

  每一层的做用python

  每层运行常见的物理设备程序员

  socket五层通信流程算法

osi每层做用django

1.应用层:为应用软件提供接口,使应用程序可以使用网络服务
常见的应用协议:http80 https443 dns53 ftp(20/21) smtp(25) pop3(110) telnet(23)

2.表示层:数据的解码和编码 加密和解密 压缩和解压缩
图片:jpg,gif
音频:MP3,wma,aac
视频:MP4,avi

3.会话层:负责创建管理和终止表示层实体之间的会话连接
在设备或节点之间提供会话控制,协调通讯过程。
并提供三种不一样的方式(单工,半双工,全双工)来组织他们之间的通讯

4.传输层(tcp/udp):负责创建端到端的链接,保证报文在端到端之间进行传输,负责数据重传,冗余校验等
服务点编址,分段与重组,链接控制,流量控制,差错控制

5.网络层:为网络设备逻辑寻址,进行路由选择,路由表维护等等
负责将分组数据从源端传输到目的端,
表明设备:路由器和三层交换机

6.数据链路层:在不可靠的物理链路上提供可靠的数据传输服务,把封装好数据帧从(一跳)节点移动到另外一节点(另外一跳)
组帧,物理编址,流量控制,差错控制,接入控制
表明设备:交换机

7.物理层:负责把逐个的比特从一跳(结点)移动到另外一跳(结点)。
定义接口和媒体的物理特性(线序、电压、电流)
定义比特的表示、数据传输速率、信号的传输模式
定义网络物理拓扑(网状、星型、环型、总线型等拓扑)
表明:集线器
View Code

 

1.tcp和udp协议的区别编程

tcp/ip协议简介
  TCP/IP协议(传输控制协议/互联网协议)不是简单的一个协议,而是一组特别的协议,包括:TCP,IP,UDP,ARP等,这些被称为子协议。在这些协议中,最重要、最著名  的就是TCP和IP。所以,大部分网络管理员称整个协议族为“TCP/IP”。

TCP协议   面向链接\可靠\慢\对传递的数据的长短没有要求   两台机器之间要想传递信息必须先创建链接   以后在有了链接的基础上,进行信息的传递   可靠 : 数据不会丢失 不会重复被接收   慢 : 每一次发送的数据还要等待结果   三次握手和四次挥手 UDP协议   无链接\不可靠\快\不能传输过长的数据0   机器之间传递信息不须要创建链接 直接发就行   不可靠 : 数据有可能丢失

TCP(Transmission Control Protocol)可靠的、面向链接的协议(eg:打电话)、传输效率低全双工通讯(发送缓存&接收缓存)、面向字节流。使用TCP的应用:Web浏览器;文件传输程序。json

UDP(User Datagram Protocol)不可靠的、无链接的服务,传输效率高(发送前时延小),一对1、一对多、多对1、多对多、面向报文(数据包),尽最大努力服务,无拥塞控制。使用UDP的应用:域名系统 (DNS);视频流;IP语音(VoIP)。flask






  import socket
  sk = socket.socket()
  sk.bind(('0.0.0.0',9090))
  sk.listen(5)
  conn,addr = sk.accept()设计模式

  accept 至关于和客户端的connect 一块儿完成了TCP的三次握手
  至于以前的sk, 它只起到一个大门的做用了, 意思是说,欢迎敲门, 进门以后我将为你生成一个独一无二的socket描述符sk!  浏览器

2.socket模块

套接字简介?
套接字起源于 20 世纪 70 年代加利福尼亚大学伯克利分校版本的 Unix,即人们所说的 BSD Unix。 所以,有时人们也把套接字称为“伯克利套接字”或“BSD 套接字”。一开始,套接字被设计用在同 一台主机上多个应用程序之间的通信。这也被称进程间通信,或 IPC。套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的

什么是socket?   创建网络通讯链接至少要一对端口号(socket)。
  socket本质是编程接口(API),对TCP
/IP的封装,TCP/IP也要提供可供程序员作网络开发所用的接口,这就是Socket编程接口;
  HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通讯的能力。
  socket又称为套接字,它是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,
  Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。

两种套接字:基于文件和面向网络的
  基于文件的:AF_UNIX
    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,能够经过访问同一个文件系统间接完成通讯
  基于网络的:AF_INIT
    (还有AF_INET6被用于ipv6,还有一些其余的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是不多被使用,或者是根本没有实现,全部地址
    家族中,AF_INET是使用最普遍的一个,python支持不少种地址家族,可是因为咱们只关心网络编程,因此大部分时候我么只使用AF_INET)

特殊意义的解释socket:
  socekt又称为‘套接字’,用于描述IP和地址端口,是一个通讯链路的句柄,应用程序一般经过套接字向网络发出请求或者应答网络请求。
  socket起源于Unix,因此也听从“一切皆文件”的基本哲学,对于文件,进行打开/读取/关闭的操做模式。
  socket就是该模式的一个实现,socket便是一种特殊的文件,一些socket函数就是对其进行的操做(读/写IO、打开、关闭)
  socket和file文件的区别:
    file模块是针对指定文件进行打开、读写、关闭操做。
    socket模块是针对服务器客户端socket进行打开、读写、关闭操做。
python中socket模块:
  地址簇:
    socket.AF_INET IPv4(默认)
    socket.AF_INET6 IPv6
    socket.AF_UNIX 只可以用于单一的Unix系统进程间通讯
  类型:
    socket.SOCK_STREAM  流式socket , for TCP (默认)
    socket.SOCK_DGRAM   数据报式socket , for UDP
tcp/ip和http的关系?
 tcp/ip协议是传输层协议,主要解决数据如何在网络中传输,而HTTP协议是应用层协议,主要解决如何包装数据。
  咱们在传输数据时,能够只使用(传输层)TCP/IP协议,可是那样的话,若是没有应用层,便没法识别数据内容。
 若是想要使传输的数据有意义,则必须使用到应用层协议。 应用层协议有不少,好比HTTP、FTP、TELNET等,也能够本身定义应用层协议。 
 WEB使用HTTP协议做应用层协议,以封装HTTP文本信息,而后使用TCP/IP作传输层协议将它发到网络上。”

socket对象

Socket对象
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

参数一:地址簇
  参数    描述
  socket.AF_INET    IPv4(默认)
  socket.AF_INET6    IPv6
  ocket.AF_UNIX    只可以用于单一的Unix系统进程间通讯
参数二:类型
  参数    描述
  socket.SOCK_STREAM    流式socket , for TCP (默认)
  socket.SOCK_DGRAM    数据报式socket , for UDP
  socket.SOCK_RAW    原始套接字,普通的套接字没法处理ICMP、IGMP等网络报文,而SOCK_RAW能够;其次,SOCK_RAW也能够处理特殊的IPv4报文;此外,利用原始套接字,能够经过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM    是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在须要执行某些特殊操做时使用,如发送ICMP报文。SOCK_RAM一般仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET    可靠的连续数据包服务

Socket类方法
  方法    描述
  s.bind(address)    将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
  sk.listen(backlog)    开始监听传入链接。backlog指定在拒绝链接以前,能够挂起的最大链接数量。
  sk.setblocking(bool)    是否阻塞(默认True),若是设置False,那么accept和recv时一旦无数据,则报错。
  sk.accept()    接受链接并返回(conn,address),其中conn是新的套接字对象,能够用来接收和发送数据。address是链接客户端的地址。
  sk.connect(address)    链接到address处的套接字。通常,address的格式为元组(hostname,port),若是链接出错,返回socket.error错误。
  sk.connect_ex(address)    同上,只不过会有返回值,链接成功时返回 0 ,链接失败时候返回编码,例如:10061
  sk.close()    关闭套接字链接
  sk.recv(bufsize[,flag])    接受套接字的数据。数据以字符串形式返回,bufsize指定最多能够接收的数量。flag提供有关消息的其余信息,一般能够忽略。
  sk.recvfrom(bufsize[.flag])    与recv()相似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
  sk.send(string[,flag])    将string中的数据发送到链接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容所有发送。
  sk.sendall(string[,flag])    将string中的数据发送到链接的套接字,但在返回以前会尝试发送全部数据。成功返回None,失败则抛出异常。内部经过递归调用send,将全部内容发送出去。
  sk.sendto(string[,flag],address)    将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
  sk.settimeout(timeout)    设置套接字操做的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。
  sk.getpeername()    返回链接套接字的远程地址。返回值一般是元组(ipaddr,port)。
  sk.getsockname()    返回套接字本身的地址。一般是一个元组(ipaddr,port)
  sk.fileno()    套接字的文件描述符
socket对象

基于tcp协议(流式socket):是两个进程之间的通讯,经过端口号来区分不一样进程

import socket
sk = socket.socket()  #)返回的用于监听和接受客户端的链接请求的套接字
# sk.bind(('192.168.16.96',9090))
sk.bind(('0.0.0.0',9090)) #能接收到全部的ip的访问
sk.listen(5)  #监听连接,而且设置监听个数,
conn,addr = sk.accept() 
# hold住,等待用户连接,accept()接受一个客户端的链接请求,并返回一个新的套接字,与客户端通讯是经过这个新的套接字上发送和接收数据来完成的。
#每一个链接进来的客户端,都会经过accept函数返回一个不一样的客户端的socket对象和属于客户端的套接字
    #bytes:是字节   b'kobe'
    #str:字符串类型  'kobe'
    #str----编码(encode)---->bytes
    #bytes-----解码(decode)--->str
    #英文字符串能够直接加b转成bytes
    #中文的必须的加上''.encode('utf-8')
#发送回复信息,在网络传输中的最小单位为字节,因此,要将数据转为字节格式
conn.send('我接受到了'.encode('utf-8'))
ret = conn.recv(4096)
print(ret.decode('utf-8'))
conn.close()  #conn.close和cilent的sk.close()是四次挥手的过程
sk.close()  #关闭socket,不接受任何client请求



import socket
sk =socket.socket()
sk.connect(('192.168.16.96',9090))  #只和server的accept对应
ret = sk.recv(1024)
print(ret.decode('utf-8'))
sk.send('你好啊'.encode('utf-8'))
sk.close()
socket服务端
import socket
sk =socket.socket()
sk.connect(('192.168.16.96',9090))  #只和server的accept对应
ret = sk.recv(1024)
print(ret.decode('utf-8'))
sk.send('你好啊'.encode('utf-8'))
sk.close()
socket客户端

基于udp协议(报文式socket)

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)  #SOCK_DGRAM指的是udp协议
sk.bind(('192.168.16.96',8081))
while True:
    msg,client_addr = sk.recvfrom(1024)
    #在udp协议中,recvfrom接收返回的时候能接收到客户端的信息msg和客户端的连接信息client_addr,
    #在tcp协议中,在等待链接的conn,addr = sk.accept()会接受到conn客户端的socket和客户端的连接信息
    print(str(client_addr)+":"+msg.decode('utf-8'))
    content = input('>>>')
    sk.sendto(content.encode('utf-8'),client_addr)
    #经过sendto和客户端的链接信息发送消息
sk.close()
socket服务器端
import socket
client_addr= ('192.168.16.96',8081)
sk = socket.socket(type=socket.SOCK_DGRAM)
while True:
    connect = input(">>>>")
    if connect.upper() !='Q':
        sk.sendto(connect.encode('utf-8'),client_addr)
        msg = sk.recv(1024).decode('utf-8')
        if msg.upper() == 'Q': break
        print(str(client_addr)+":"+msg)
    else:
        break
socket客户端
注意:
    1.tcp协议服务端是经过accept来创建连接,获取客户端的连接信息
         conn,addr = sk.accept()
      经过recv来获取客户端消息
         msg = sk.recv(1024).decode('utf-8')
    经过send发送信息
     conn.send('hello'.encode('utf-8'))

    客户端须要链接服务端地址
     sk.connect(服务器地址和端口)
   客户端发送信息
     sk.send('你好'.encode('utf-8'))
    客户端接收信息
     msg = sk.recv(1024).deocde('1024')
2.udp协议服务器是经过recvfrom来获取客户端发送的信息和客户端连接的信息
      msg,addr1 = sk.recvfrom(1024)
    经过sendto发送信息给客户端,要指定客户端信息
      sk.sendto('hello'.encode('utf-8'),addr1)

    udp客户端不须要连接服务器端,是经过sendto发送信息
      sk.sendto('你好'.encode('utf-8'),服务器地址和端口)
    经过recv来获取信息
      msg = sk.recv(1024).decode('utf-8')
 

 3.tcp黏包

什么叫作黏包?

通常所谓的TCP粘包是在一次接收数据不能彻底地体现一个完整的消息数据。

为何只有TCP通信存在粘包?

主要缘由是TCP是以流的方式来处理数据,而且能发送大量的数据,再加上网络上MTU的每每小于在应用处理的消息数据,因此就会引起一次接收的数据没法知足消息的须要,致使粘包的存在。

TCP协议拆包机制

当发送端缓冲区的长度大于网卡的(最大传输单元)时,tcp会将此次发送的数据拆成几个数据包发送出去。 
MTU是Maximum Transmission Unit的缩写。意思是(网卡)网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。
若是本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生不少数据包碎片,增长丢包率,下降网络速度。
同时执行多条命令以后,获得的结果极可能只有一部分,在执行其余命令时又会接到以前执行的另一部分结果,这种就是黏包。

 面向流的通讯特色和Nagle(优化)算法

TCPtransport control protocol,传输控制协议)是面向链接的,面向流的,提供高可靠性服务。 收发两端(客户端和服务器端)都要有一一成对的socket,所以,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将屡次间隔较小且数据量小的数据,合并成一个大的数据块,而后进行封包。 这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通讯是无消息保护边界的。 
对于空消息:tcp是基于数据流的,因而收发的消息不能为空,这就须要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即使是你输入的是空内容(直接回车),也能够被发送,udp协议会帮你封装上消息头发送过去。

可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端老是在收到ack时才会清除缓冲区内容。数据是可靠的,可是会粘包。
此外,发送方引发的粘包是由TCP协议自己形成的,TCP为提升传输效率,发送方每每要收集到足够多的数据后才发送一个TCP段。若连续几回须要send的数据都不多,
一般TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

 黏包的两种状况

1,发送方的缓存机制:发送端须要等缓冲区满才发送出去,形成黏包(发送数据时间间隔很短,数据很小,会合到一块儿,产生黏包)
    连续send两次且数据很小
2,接收方的缓存机制:接收不及时接收缓冲区的包,形成多个包接收(客户端发送一段数据,服务端只收了一小部分,服务端下次再收的时候仍是从缓冲区拿走上次剩余的数据,产生黏包。)
    连续recv两次且第一个recv接收的数据小

 黏包处理

# -*- coding: utf-8 -*- 
# @Time    : 2019/4/10 16:58 
# @Author  : p0st
# @Site    :  
# @File    : 传递大文件server.py
# @Software: PyCharm
import time
import json
import struct
import socket
start_time = time.time()
sk = socket.socket()
ip_addr = (('127.0.0.1',9999))
sk.bind(ip_addr)
sk.listen(5)
conn,addr = sk.accept()

num = conn.recv(4)
l_num = struct.unpack('i',num)[0]  #bytes类型的json的长度为45,由于struct.unpack是元组类型的
l_dic = conn.recv(l_num).decode('utf-8')  #将bytes类型的json转成字符串类型的json
dic = json.loads(l_dic)
filesize = dic['file_size']
with open(dic['file_name'],'wb') as info:
        while filesize>=1024:
            content = conn.recv(1024)
            info.write(content)
            filesize -=1024
        else:
            content = conn.recv(filesize)
            if content:
                info.write(content)
conn.close()
sk.close()
print(time.time()-start_time)
发送大文件服务端
# -*- coding: utf-8 -*- 
# @Time    : 2019/4/10 17:03 
# @Author  : p0st
# @Site    :  
# @File    : 传递大文件client.py
# @Software: PyCharm
import json
import struct
import socket
import os
sk = socket.socket()
ip_addr = (('127.0.0.1',9999))
sk.connect_ex(ip_addr)

file_path = input("请输入文件路径:>>>").strip()
file_name = os.path.basename(file_path) #获取文件名字
file_size = os.path.getsize(file_path)  #获取文件大小

s_dic = {'file_name':file_name,'file_size':file_size}
j_s_dic = json.dumps(s_dic)   #将字典序列化成json
b_s_dic = j_s_dic.encode('utf-8')  #将json转成bytes类脑,在网上传输
l_s_dic = len(b_s_dic)   #常看bytes类型的长度

lalala = struct.pack('i',l_s_dic)  #将bytes类型的长度经过struct的pack方法变成4个字节
sk.send(lalala)
sk.send(b_s_dic)
with open(file_path,'rb') as info:
    while file_size >= 1024:
        content = info.read(1024)
        sk.send(content)
        file_size -= 1024
    else:
        content = info.read(file_size)
        if content:
            sk.send(content)

sk.close()






#1.将字典转成json,再转成bytes类型,查看其长度发送给服务器端
#2.将bytes类型的发送给服务器
发送大文件客户端

发送大文件带注释版本

# -*- coding: utf-8 -*- 
# @Time    : 2019/7/18 16:50 
# @Author  : p0st
# @Site    :  
# @File    : server.py
# @Software: PyCharm
import struct
import socket
import hashlib
import json
import os
my_file_path = os.path.join(os.path.dirname(__file__),'file')

def socket_server():
    ip_port = ('127.0.0.1',8080)
    server = socket.socket()  # 建立socket对象
    server.bind(ip_port) # 绑定ip地址和端口
    server.listen(5)  # 设置监听对象为5个
    conn,addr = server.accept() # 设置等待链接


    # 1.接收4个字节的固定长度
    four_bytes = conn.recv(4)

    # 2.利用struct模块进行反解
    head_len = struct.unpack('i',four_bytes)[0]

    # 3.接收bytes类型的数据
    head_dic_json_bytes = conn.recv(head_len)

    # 4.将bytes类型的数据转成json类型的数据
    head_dic_json = head_dic_json_bytes.decode('utf-8')

    # 5.将json类型的数据转成字典形式的数据
    head_dic = json.loads(head_dic_json)

    # 6.边传输编计算MD5的值
    md5 = hashlib.md5()
    with open(os.path.join(my_file_path,head_dic['new_file_name']),mode='wb') as info:
        while 1:
            data = conn.recv(1024)
            if data:
                md5.update(data)
                info.write(data)
            else:
                info.close()
                if md5.hexdigest() == head_dic['md5']:
                    print('传输成功')
                    break
                else:
                    print('传输失败')
                    break
if __name__ == '__main__':
    socket_server()
服务端
# -*- coding: utf-8 -*- 
# @Time    : 2019/7/18 16:50 
# @Author  : p0st
# @Site    :  
# @File    : client.py
# @Software: PyCharm
import os
import json
import hashlib
import socket
import struct
my_file_path = os.path.join(os.path.dirname(__file__),'1.png')

def socket_client():
    ip_port = ('127.0.0.1',8080)
    client = socket.socket() # 建立socket对象
    client.connect(ip_port)

    # 1.制做字典了类型的数据
    head_dic = {
        'md5': md5(my_file_path),
        'file_name': os.path.basename(my_file_path),
        'file_size': os.path.getsize(my_file_path),
        'new_file_name':'11.png',
    }
    # 2.将字典转换成json的数据类型
    head_dic_json = json.dumps(head_dic)

    # 3.获取bytes类型的数据
    head_dic_json_baytes = head_dic_json.encode('utf-8')

    # 4.获取bytes类型的字节数
    head_len = len(head_dic_json_baytes)

    # 5.将bytes类型的总字节数转化成固定的4个字节
    four_bytes = struct.pack('i',head_len)

    # 6.发送4个固定字节
    client.send(four_bytes)

    # 7.发送head数据
    client.send(head_dic_json_baytes)

    # 8.发送总数据
    with open(my_file_path,mode='rb') as info:
        while 1:
            data = info.read(1024)
            if data:
                client.send(data)
            else:
                break
def md5(file_path):
    md5 = hashlib.md5()
    with open(file_path, mode='rb') as info:
        while 1:
            data = info.read(1024)
            if data:
                md5.update(data)
            else:
                break
        return md5.hexdigest() #31e03dc9b5a06c91f63dbfee470f2c58

if __name__ == '__main__':
    socket_client()
客户端

使用struct解决黏包:该模块能够把一个类型,如数字,转成固定长度的bytes

咱们知道长度数字能够被转换成一个标准大小的4字节数字。所以能够利用这个特色来预先发送数据长度。

发送时                                接收时
  先发送struct转换好的数据长度4字节        先接受4个字节使用struct转换成数字来获取要接收的数据长度
  再发送数据                            再按照长度接收数据

理解下struct模块,经过该模块能够将一个类型,如数字,转成固定长度的bytes类型

import struct
# 将一个数字转化成等长度的bytes类型。
ret = struct.pack('i', 183346)
print(ret, type(ret), len(ret))

# 经过unpack反解回来
ret1 = struct.unpack('i',ret)[0]
print(ret1, type(ret1), len(ret1))


# 可是经过struct 处理不能处理太大
ret = struct.pack('l', 4323241232132324)
print(ret, type(ret), len(ret))  # 报错

 并发的socketserver

import time
import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        for i in range(200):
            conn.send(('hello%s'%i).encode('utf-8'))
            print(conn.recv(1024))
            time.sleep(0.5)
            print(conn)


if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999
    # 设置allow_reuse_address容许服务器重用地址
    socketserver.TCPServer.allow_reuse_address = True
    # 建立一个server, 将服务地址绑定到127.0.0.1:9999
    server = socketserver.TCPServer((HOST, PORT),Myserver)
    # 让server永远运行下去,除非强制中止程序
    server.serve_forever()


#sk.setblocking(False)  #默认不阻塞,不阻塞模型,django和flask等等,setblocking作到和socketserver对tcp协议的非阻塞
# 非阻塞模型是一个突破,udp协议不用阻塞和不阻塞,udp协议能同时提供对多个客户端进行链接
server
import socket

HOST, PORT = "127.0.0.1", 9999
data = "hello"

# 建立一个socket连接,SOCK_STREAM表明使用TCP协议
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))          # 连接到客户端
    sock.sendall(bytes(data + "\n", "utf-8")) # 向服务端发送数据
    received = str(sock.recv(1024), "utf-8")# 从服务端接收数据

print("Sent:     {}".format(data))
print("Received: {}".format(received))
client

数据报和数据流的区别

1.报文(message)
咱们将位于应用层的信息分组称为报文。报文是网络中交换与传输的数据单元,也是网络传输的单元。报文包含了将要发送的完整的数据信息,其长短不需一致。报文在传输过程当中会不断地封装成分组、包、帧来传输,封装的方式就是添加一些控制信息组成的首部,那些就是报文头。

2.报文段(segment)

一般是指起始点和目的地都是传输层的信息单元。

3.分组/包(packet)
分组是在网络中传输的二进制格式的单元,为了提供通讯性能和可靠性,每一个用户发送的数据会被分红多个更小的部分。在每一个部分的前面加上一些必要的控制信息组成的首部,有时也会加上尾部,就构成了一个分组。它的起始和目的地是网络层。

4.数据报(datagram)
面向无链接的数据传输,其工做过程相似于报文交换。采用数据报方式传输时,被传输的分组称为数据报。一般是指起始点和目的地都使用无链接网络服务的的网络层的信息单元。

5.帧(frame)
帧是数据链路层的传输单元。它将上层传入的数据添加一个头部和尾部,组成了帧。它的起始点和目的点都是数据链路层。

6.数据单元(data unit)

指许多信息单元。经常使用的数据单元有服务数据单元(SDU)、协议数据单元(PDU)。

SDU是在同一机器上的两层之间传送信息。PDU是发送机器上每层的信息发送到接收机器上的相应层(同等层间交流用的)。

 

应用层——消息

传输层——数据段/报文段(segment) (注:TCP叫TCP报文段,UDP叫UDP数据报,也有人叫UDP段)

网络层——分组、数据包(packet)

链路层——帧(frame)

物理层——P-PDU(bit)



其实,segment,datagram,packet,frame是存在于同条记录中的,是基于所在协议层不一样而取了不一样的名字。咱们能够用一个形象的例子对数据包的概念加以说明:咱们在邮局邮寄产品时,虽然产品自己带有本身的包装盒,可是在邮寄的时候只用产品原包装盒来包装显然是不行的。必须把内装产品的包装盒放到一个邮局指定的专用纸箱里,这样才可以邮寄。这里,产品包装盒至关于数据包,里面放着的产品至关于可用的数据,而专用纸箱就至关于帧,且一个帧中一般只有一个数据包。
 

7.TCP数据流(TCP stream)

Wireshark中是这么定义的:相同四元组(源地址,源端口,目的地址,目的端口)的包就为一条TCP流,即一条流有不少个包。
数据报和数据流

缓冲区

每一个 socket 被建立后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

write()/send() 并不当即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就能够成功返回,无论它们有没有到达目标机器,也无论它们什么时候被发送到网络,这些都是TCP协议负责的事情。

TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,屡次写入的数据被一次性发送到网络,这取决于当时的网络状况、当前线程是否空闲等诸多因素,不禁程序员控制。

read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。

这些I/O缓冲区特性可整理以下:

1.I/O缓冲区在每一个TCP套接字中单独存在;
2.I/O缓冲区在建立套接字时自动生成;
3.即便关闭套接字也会继续传送输出缓冲区中遗留的数据;
4.关闭套接字将丢失输入缓冲区中的数据。

输入输出缓冲区的默认大小通常都是 8K,能够经过 getsockopt() 函数获取:

1.unsigned optVal;
2.int optLen = sizeof(int);
3.getsockopt(servSock, SOL_SOCKET, SO_SNDBUF,(char*)&optVal, &optLen);
4.printf("Buffer length: %d\n", optVal);

socket缓冲区解释
View Code

缓冲区的做用

先结束的进程能够把结果放入缓冲区内,进行下面的工做,然后作完的进程能够从缓冲区内取出原来的数据继续工做。
缓冲区的做用是:在高速和低速设备之间起一个速度平滑做用;暂时存储数据;常常访问的数据能够放进缓冲区,减小对慢速设备的访问以提升系统的效率。提升上传和下载的速度

缓冲(buffering)
利用存储区缓解数据到达速度与离去速度不一致而采用的技术称为缓冲,此时同一数据只包含一个拷贝。例如:操做系统以缓冲方式实现设备的输入和输出操做主要是缓解处理机与设备之间速度不匹配的矛盾,从而提升资源利用律和系统效率。
View Code

 

返回系列

相关文章
相关标签/搜索