1.tcp和udp协议的区别

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

UDP协议
  无连接\不可靠\快\不能传输过长的数据0
  机器之间传递信息不需要建立连接 直接发就行
  不可靠 : 数据有可能丢失


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

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

2.socket模块

什么是socket?
  建立网络通信连接至少要一对端口号(socket)。  socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;  HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

两种套接字:基于文件和面向网络的  基于文件的:AF_UNIX  基于网络的:AF_INIT

特殊意义的解释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协议拆包机制

当发送端缓冲区的长度大于网卡的MTU时,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类型的发送给服务器

发送大文件客户端

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

我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。

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

并发的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)

server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()

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

返回系列

python进阶之 网络编程的更多相关文章

  1. Python进阶之网络编程

    网络通信 使用网络的目的 把多方链接在一起,进行数据传递: 网络编程就是,让不同电脑上的软件进行数据传递,即进程间通信: ip地址 ip地址概念和作用 IP地址是什么:比如192.168.1.1 这样 ...

  2. python高级之网络编程

    python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及其源码分析 1.网络通信概念 说 ...

  3. 第六篇:python高级之网络编程

    python高级之网络编程   python高级之网络编程 本节内容 网络通信概念 socket编程 socket模块一些方法 聊天socket实现 远程执行命令及上传文件 socketserver及 ...

  4. Python进阶:函数式编程实例(附代码)

    Python进阶:函数式编程实例(附代码) 上篇文章"几个小例子告诉你, 一行Python代码能干哪些事 -- 知乎专栏"中用到了一些列表解析.生成器.map.filter.lam ...

  5. Python之路 - 网络编程之粘包

    Python之路 - 网络编程之粘包 粘包

  6. Python之路 - 网络编程初识

    Python之路 - 网络编程初识 前言

  7. Python进阶之面向对象编程

    面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...

  8. 周末班:Python基础之网络编程

    一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好.但是如果这两个程序之间想要传递一个数据, ...

  9. python之路——网络编程

    一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好.但是如果这两个程序之间想要传递一个数据, ...

随机推荐

  1. 印象笔记中的美人鱼 mermaid

    美人鱼 mermaid 是印象笔记中Markdown模式下新增的一种代码模式,它能支持更多的高级图表功能,如流程图.甘特图.时序图. 我最喜欢的应该是甘特图,最惊喜的是流程图. 当然,印象笔记还支持其 ...

  2. C语言——数组名、取数组首地址的区别(一)

    目录: 1. 开篇 2. 论数组名array.&array的区别 3. array.&array的区别表现在什么地方 4. 讨论 5. 参考 1.开篇 很多博客和贴吧都有讨论这个话题, ...

  3. python内建时间模块 time和datetime

    时间模块 UTC(Coordinated Universal Time,世界协调时)亦即格林威治天文时间,世界标准时间.在中国为UTC+8.DST(Daylight Saving Time)即夏令时. ...

  4. springboot-aop

    AOP(面向切面编程)是Spring的两大核心功能之一,功能非常强大,为解耦提供了非常优秀的解决方案. 现在就以springboot中aop的使用来了解一下如何使用aop. 写几个简单的Spring ...

  5. there was an error running the selected code generator unable to retrieve metadata for

    there was an error running the selected code generator unable to retrieve metadata for PROBLEM: I ha ...

  6. [hadoop] kettle spoon 基础使用 (txt 内容抽取到excel中)

    spoon.bat 启动kettle. 测试数据 1. 新建转换 输入中选择文本文件输入 双击设置文本输入 字符集.分隔符设置 获取对应的字段,预览记录. 拖入 excel输出,设置转换关系 设置输出 ...

  7. JavaScript Practices

    不定时更新:https://github.com/zhengyeye/JS-practices Day1:关于创建对象的几种方式: Day3:原型 早前一篇:https://www.cnblogs.c ...

  8. 关闭win10一切

    狂客原创,转载请注明来源 关闭更新 注册表(以管理员身份运行) 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\     Start值 ...

  9. ssh: connect to host xx.xx.xxx.xxx port 22: Connection refused

    执行 ssh root@47.94.142.215 报错 首先要开启ssh 系统偏好设置-共享 选上远程登陆 选上远程管理 检测openssh-server是否安装成功 ,安装成功的如下 ps -e ...

  10. jquery实现同时展示多个tab标签+左右箭头实现来回滚动(美化版增加删除按钮)

    闲聊 前段时间小颖分享了:jquery实现同时展示多个tab标签+左右箭头实现来回滚动文章,引入项目后,我们的组长说样子太丑了,小颖觉得还好啊,要不大家评评理,看下丑不丑?无图无真相,来上图: 看吧其 ...