C/S架构的软件(软件属于应用层)是基于互联网举行通讯的,即人们所说的 BSD Unix

unix一切皆文件,基于文件的套接字调用的正是底层的文件系统来取数据,四个套接字进程运维在同一机器,能够因此拜访同七个文件系统直接完结通讯

② 、基于网络项指标套接字家族

套接字家族的名字:AF_INET

(还有AF_INET6被用来ipv6,还有一些其余的地址家族,不过,他们依然是只用于某些平台,要么正是已经被遗弃,或然是很少被应用,可能是一向没有落到实处,全部地方家族中,AF_INET是使用最普遍的1个,python帮衬很种种地点家族,不过由于大家只关怀互联网编制程序,所以超过56%时候小编么只行使AF_INET)

 

粘包现象:

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

套接字家族的名字:AF_UNIX

unix一切皆文件,基于文件的套接字调用的正是底层的文件系统来取数据,三个套接字进度运维在同一机器,可以透过拜访同三个文件系统直接完毕通讯

 

四,基于tcp的套接字

肆 、套接字发展史及分类

套接字源点于 20 世纪 70 时期佛蒙特大学伯克利分校版本的
Unix,即人们所说的 BSD Unix。
因而,有时人们也把套接字称为“Berkeley套接字”或“BSD
套接字”。一早先,套接字被规划用在同
一台主机上七个应用程序之间的报纸发表。那也被称经过间通信,或
IPC。套接字有三种(或然叫做有多个种族),分别是遵照文件型的和依据网络型的。 

 

tcp服务端

伍 、套接字工作流程

     
生活中的场景,你要打电话给贰个对象,先拨号,朋友听到电话铃声后提起电话,这时你和您的情侣就确立起了连接,就能够说话了。等沟通结束,挂断电话截至本次交谈。    

活着中的场景就解释了那工作规律,只怕TCP/IP协议族正是诞生于生存中,那也不自然。

图片 1

socket例子:

1.服务端与客户端的日常化通讯。

图片 2图片 3

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')

conn,client_addr=phone.accept()
print(conn,client_addr)

data=conn.recv(1024)
conn.send(data.upper())
print('client data:<%s>'%data)

conn.close()
phone.close()

服务端

图片 4图片 5

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

phone.send('hello'.encode('utf-8'))
data1 = phone.recv(1024)
print('server back res:<%s>'%data1)

phone.close()

客户端

首先:
服务端 先开始运行,等待接收,
之后,客户端运行,向服务端发送信息。

结果如下:
服务端:
really ==== go!!
<socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53789)> ('127.0.0.1', 53789)
client data:<b'hello'>

客户端:
server back res:<b'HELLO'>

客户端发了 hello  给服务端, 服务端收到信息,做了 大写化处理,返回给客户端。

2.服务端与客户端的健康通讯。socket通讯循环

图片 6图片 7

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')

conn,client_addr=phone.accept()
print(conn,client_addr)

while True:  #通信循环
    data=conn.recv(1024)
    # print('server has recv')
    conn.send(data.upper())
    print('client data:<%s>'%data)

conn.close()
phone.close()

服务端

图片 8图片 9

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #如果cmd为空,继续发
    phone.send(cmd.encode('utf-8'))
    print('====> has send')
    data = phone.recv(1024)
    print('server back res:<%s>'%data)

phone.close()

客户端

首先:
这里比上一个例子,优化了,这里设置了input,可以自己输入。
添加了个循环,当客户端输入为空,不在报错,而是需要继续输入。

结果如下:
服务端:
really ==== go!!
<socket.socket fd=452, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53865)> ('127.0.0.1', 53865)
client data:<b'1'>
client data:<b'2'>
client data:<b'3'>
client data:<b'a'>
client data:<b'b'>
client data:<b'c'>

客户端:
>>:1
====> has send
server back res:<b'1'>
>>:2
====> has send
server back res:<b'2'>
>>:3
====> has send
server back res:<b'3'>
>>:a
====> has send
server back res:<b'A'>
>>:b
====> has send
server back res:<b'B'>
>>:c
====> has send
server back res:<b'C'>

注意:
这里服务端在接到客户端的额信息是,只做了加大化吃力,所以把abc处理后为ABC返回给客户端。

3.服务端与客户端的常规通讯。socket链接循环。

图片 10图片 11

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('really ==== go!!')
while True:  #链接循环
    conn,client_addr=phone.accept()
    print(conn,client_addr)

    while True:  #通信循环
        try:
            data=conn.recv(1024)
            # print('server has recv')
            conn.send(data.upper())
            print('client data:<%s>'%data)
        except Exception:
            break
    conn.close()
phone.close()

服务端

图片 12图片 13

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #如果cmd为空,继续发
    phone.send(cmd.encode('utf-8'))
    data = phone.recv(1024)
    print('server back res:<%s>'%data)
phone.close()

客户端1

图片 14图片 15

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #如果cmd为空,继续发
    phone.send(cmd.encode('utf-8'))
    print('====> has send')
    data = phone.recv(1024)
    print('server back res:<%s>'%data)

phone.close()

客户端2

首先,这里加的链接循环是为了防止,当我们有多个客户端时,
要关闭其中一个,而不导致整个程序出错。
在没做链接循环前,当我们关闭了其中一个客户端,服务端那里是不能在运行的。

输出结果:
服务端:
really ==== go!!
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53898)> ('127.0.0.1', 53898)
client data:<b'1'>
client data:<b'2'>
client data:<b'a'>
client data:<b'b'>
client data:<b'c'>
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
<socket.socket fd=384, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 53911)> ('127.0.0.1', 53911)
client data:<b'a'>
client data:<b's'>
client data:<b'd'>

客户端1:
>>:1
server back res:<b'1'>
>>:2
server back res:<b'2'>
>>:3
server back res:<b'3'>
>>:a
server back res:<b'A'>
>>:b
server back res:<b'B'>
>>:c
server back res:<b'C'>


客户端2:
>>:a
====> has send
server back res:<b'A'>
>>:s
====> has send
server back res:<b'S'>
>>:d
====> has send
server back res:<b'D'>

4.socket仿照ssh远程执行。

图片 16图片 17

import subprocess
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',8081))
phone.listen(5)
print('really ==== go!!')
while True:  #链接循环
    conn,client_addr=phone.accept()
    print(conn,client_addr)

    while True:  #通信循环
        try:
            cmd=conn.recv(1080)
            if not cmd: break  #针对linux
            #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
            #。。。
            #发送命令结果
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,  # 正确
                                   stderr=subprocess.PIPE  # 错误
                                   )
            stdout = res.stdout.read()
            stderr = res.stderr.read()
            conn.send(stdout+stderr)
        except Exception:
            break
    conn.close()
phone.close()

服务端

图片 18图片 19

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect  (('127.0.0.1',8081))

while True:
    cmd = input('>>:').strip()
    if not cmd:continue     #如果cmd为空,继续发
    phone.send(cmd.encode('utf-8'))
    cmd_res = phone.recv(1080)
    print(cmd_res.decode('gbk'))
phone.close()

客户端

图片 20图片 21

说明:
1.客户端远程执行服务端。
2.登录的是windows系统,用的是‘gbk’ 编码。


服务端:
really ==== go!!
<socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8081), raddr=('127.0.0.1', 53955)> ('127.0.0.1', 53955)


客户端:
>>:dir
 驱动器 E 中的卷没有标签。
 卷的序列号是 0001-0682

 E:\zbk\work_\work_8.21 socket模拟ssh远程执行 的目录

2017/08/21  19:25    <DIR>          .
2017/08/21  19:25    <DIR>          ..
2017/08/21  19:25               333 客户端1.py
2017/08/21  16:29               367 客户端2.py
2017/08/21  19:25               966 服务端2.py
2017/08/21  19:05               413 模块subprocess.py
               4 个文件          2,079 字节
               2 个目录 266,249,355,264 可用字节

>>:ipconfig /all

Windows IP 配置

   主机名  . . . . . . . . . . . . . : DESKTOP-0QR7V9H
   主 DNS 后缀 . . . . . . . . . . . : 
   节点类型  . . . . . . . . . . . . : 混合
   IP 路由已启用 . . . . . . . . . . : 否
   WINS 代理已启用 . . . . . . . . . : 否

以太网适配器 以太网:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Realtek PCIe GBE Family Controller
   物理地址. . . . . . . . . . . . . : 80-FA-5B-3C-8F-54
   DHCP 已启用 . . . . . . . . . . . : 是
   自动配置已启用. . . . . . . . . . : 是

无线局域网适配器 本地连接* 1:

   媒体状态  . . . . . . . . . . . . : 媒体已断开连接
   连接特定的 DNS 后缀 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft Wi-Fi Direct Virtual Adapter
   物理地址. . . . . . . . . . . . . : 70-1C-E7-32-BC-D5
   DHCP 已启用 . . . . . . . . . . . : 是
   自动配置已启用. . . . . . . . . . : 是

无线局域网适配器 WLAN:

   连接特定的 DNS 后缀 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Intel(R) Dual Band Wireless-
>>:

出口结果

5.socket  消除粘包难题。

图片 22图片 23

import struct
import subprocess
from socket import *

phone = socket(AF_INET,SOCK_STREAM)
phone.bind(('127.0.0.1',8080))
phone.listen(5)
print('ready go !!')

while True:
    conn,client.addr=phone.accept()
    print(conn,client_addr)
    while True:
        try:
        cmd = conn.recv(1024)
        if not cmd : break
        res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
                                            stdout = stdout.subprocess.PIPE,
                                            stderr = stderr.subprocess.PIPE,)
        stdout  = res.stdout.read()
        stderr = res.stderr.read()
        header = struct.pack('i',len(stdout)+len(stderr))
        conn.send(header)
        conn.send(stdout)
        conn.send(stderr)
        except Exception:
            break
    conn.close()
phone.close()

服务端

图片 24图片 25

import struct
from socket import *

phone = socket(AF_INET,SOCK_STREAM)
phone.connect(('127.0.0.1',8080))

while True:
    cmd = input('>>:').strip()
    if not cmd : continue
    phone.send(cmd.encode('utf-8'))
    header_struct = phone.recv(4)
    unpack_res = struct.unpack('i',header_struct)
    total_size = unpack_res[0]
    total_data = b''
    recv_size = 0
    while recv_size < total_size:
        recv_data = phone.recv(1024)
        recv_size += len(recv_data)
        total_data += recv_data
    print(total_data.decode('gbk'))
phone.close()

客户端

 6.json 消除粘包问题。

图片 26图片 27

import socket
import subprocess
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8082)) #绑定手机卡
phone.listen(5) #开机

print('starting...')
while True: #链接循环
    conn,client_addr=phone.accept() #等电话 (链接,客户的的ip和端口组成的元组)
    print('-------->',conn,client_addr)

    #收,发消息
    while True:#通信循环
        try:
            cmd=conn.recv(1024)
            if not cmd:break #针对linux
            #执行cmd命令,拿到cmd的结果,结果应该是bytes类型
            #。。。。
            res = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            #制作报头
            header_dic = {
                'total_size': len(stdout)+len(stderr),
                'filename': None,
                'md5': None}

            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode('utf-8')
            #发送阶段
            #先发报头长度
            conn.send(struct.pack('i',len(header_bytes)))
            #再发报头
            conn.send(header_bytes)

            #最后发送命令的结果
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close() #挂电话
phone.close() #关机

服务端

图片 28图片 29

import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
phone.connect(('127.0.0.1',8082)) #绑定手机卡

#发,收消息
while True:
    cmd=input('>>: ').strip()
    if not cmd:continue

    phone.send(cmd.encode('utf-8'))
    #先收报头的长度
    header_len=struct.unpack('i',phone.recv(4))[0]

    #再收报头
    header_bytes=phone.recv(header_len)
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    total_size=header_dic['total_size']

    #最后收数据
    recv_size=0 #10241=10240+1
    total_data=b''
    while recv_size < total_size:
        recv_data=phone.recv(1024)
        recv_size+=len(recv_data)
        total_data+=recv_data
    print(total_data.decode('gbk'))
phone.close()

客户端

 

  Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计格局中,Socket其实就是1个外衣格局,它把复杂的TCP/IP协议族隐藏在Socket接口前面,对用户来说,一组不难的接口就是全部,让Socket去组织数据,以合乎内定的说道。

贰 、OSI七层模型

tcp客户端

 ① 、服务端和客户端

BS架构 (腾讯通软件:server+client)

CS架构 (web网站)

 

C/S架构与socket的关系:

我们上学socket正是为着形成C/S架构的付出

 

三、socket是什么?

Socket是应用层与TCP/IP协议族通讯的中档软件抽象层,它是一组接口。在设计格局中,Socket其实就是三个外衣情势,它把纷纭的TCP/IP协议族隐藏在Socket接口后边,对用户来说,一组大约的接口正是整套,让Socket去组织数量,以符合内定的磋商。

故而,我们无需深切掌握tcp/udp协议,socket已经为大家封装好了,大家只需求依据socket的规定去编程,写出的主次自然便是安分守纪tcp/udp标准的。

怎样是粘包:

网络球组织议依据效益不一分为osi七层或tcp/ip五层或tcp/ip四层

图片 30

上学socket一定要先读书网络球组织议:

1.率先:本节科目的指标就是教会你怎样依据socket编程,来开发一款融洽的C/S架构软件

2.其次:C/S架构的软件(软件属于应用层)是依照互连网开展通讯的

3.然后:互连网的中坚即一堆协议,协议即正式,你想付出一款基于互联网通信的软件,就亟须根据这么些标准。

4.末尾:就让大家从那几个标准开头探讨,开启大家的socket编程之旅

图片 31

socket:就是置身 应用层和传导层
之间。socket帮大家封装了一四种协议,统一标准。

图片 32

焚薮而田粘包:

图片 33图片 34

图片 35图片 36

 

import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',2224))

while True:
    cmd=input('>>>>').strip()
    if not cmd: continue
    phone.send(cmd.encode('utf-8'))
    cmd_res=phone.recv(1024)
    print(cmd_res.decode('gbk'))
phone.close()

  在发送数据以前,头阵送贰个包括数据长度的报头过去,从而就清楚了接受新闻的限度。

**  粘包是指发送方发送的多少包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。**

基于网络项指标套接字家族

消除粘包后的ssh客户端

一,什么是socke

 五,粘包(TCP)

(还有AF_INET6被用于ipv6,还有部分其余的地方家族,可是,他们依然是只用于有些平台,要么就是曾经被丢弃,恐怕是很少被应用,大概是素有没有落到实处,全部地方家族中,AF_INET是运用最广大的一个,python支持很各样地点家族,然则由于大家只关心网络编制程序,所以大部分时候笔者么只利用AF_INET)

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

 

 

三,套接字常用函数

 

import socket
import subprocess
import struct

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',2224))
phone.listen(5)
while True:
    print('Starting........')
    conn,client_addr=phone.accept()
    print(conn,client_addr)
    while True:
        try:
            cmd=conn.recv((1024))
            res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()

            header=struct.pack('i',len(stdout)+len(stderr))
            conn.send(header)
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close()
phone.close()

 

套接字源点于 20 世纪 70 时代亚拉巴马大学Berkeley分校版本的
Unix,即人们所说的 BSD Unix。
由此,有时人们也把套接字称为“伯克利套接字”或“BSD
套接字”。一早先,套接字被设计用在同
一台主机上三个应用程序之间的简报。那也被称经过间通信,或
IPC。套接字有两种(也许叫做有八个种族),分别是依照文件型的和依照互联网型的。 

 

套接字家族的名字:AF_INET

  粘包难点关键依然因为接收方不知底音信之间的尽头,不知底一遍性领取多少字节的多寡所导致的。

 

二,套接字发展史及分类

  也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上
的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序而程序的pid是同一台机器上不同进程或者线程的标识

 

套接字家族的名字:AF_UNIX

服务端套接字函数
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()      得到阻塞套接字操作的超时时间

面向文件的套接字的函数
s.fileno()          套接字的文件描述符
s.makefile()        创建一个与该套接字相关的文件
import socket
import struct

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',2224))

while True:
    cmd=input('>>>>').strip()
    if not cmd: continue
    phone.send(cmd.encode('utf-8'))
    head_res=phone.recv(4)
    len_res=struct.unpack('i',head_res)[0]
    len_size=0
    res=b''
    while len_size < len_res:
        data=phone.recv(1024)
        len_size+=len(data)
        res+=data
    print(res.decode('gbk'))
phone.close()

赶尽杀绝粘包后的ssh服务端

 

 

import socket
import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(('127.0.0.1',2224))
phone.listen(5)
while True:
    print('Starting........')
    conn,client_addr=phone.accept()
    print(conn,client_addr)
    while True:
        try:
            cmd=conn.recv((1024))
            res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
            stdout=res.stdout.read()
            stderr=res.stderr.read()
            conn.send(stdout+stderr)
        except Exception:
            break
    conn.close()
phone.close()

相关文章