Socket

一、Socket

  1.UDP协议

    数据报协议

    无需建立双向通道,数据传输是不安全

    将内存中的数据直接发送出去,不会做保留

    早期的qq

    ps:TCP协议类似于打电话,UDP协议类似于发短信

  2.TCP特点:

    会将数据量比较小的并且时间间隔比较短的数据一次性打包发送给对方

  3.OSI协议补充

  4.解决端口占用问题

from socket import SOL_SOCKET,SO_REUSEADDR    
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加

  5.socket通信简单版本

    服务端

import socket

server = socket.socket()  # 买手机 不传参数默认用的就是TCP协议
server.bind(('127.0.0.1',8080))  # bind((host,port))  插电话卡  绑定ip和端口
server.listen(5)  # 开机    半连接池

conn, addr = server.accept()  # 接听电话  等着别人给你打电话     阻塞
data = conn.recv(1024)  # 听别人说话 接收1024个字节数据          阻塞
print(data)
conn.send(b'hello baby~')  # 给别人回话

conn.close()  # 挂电话
server.close()  # 关机

    客户端

import socket

client = socket.socket()  # 拿电话
client.connect(('127.0.0.1',8080))  # 拨号   写的是对方的ip和port

client.send(b'hello world!')  # 对别人说话
data = client.recv(1024)  # 听别人说话
print(data)

client.close()  # 挂电话

   总结:

    127.0.0.1本机回还地址

      只能自己识别自己,其他人无法访问

    send与recv对应

      不要出现两边都是相同的情况

      recv是跟内存要数据(至于数据的来源,你无需考虑)

  6.半连接池的理解

     

  7.连接循环+通信循环

    服务端

import socket

"""
服务端
    固定的ip和port
    24小时不间断提供服务
"""
server = socket.socket()  # 生成一个对象
server.bind(('127.0.0.1',8080))  # 绑定ip和port
server.listen(5)  # 半连接池

while True:
    conn, addr = server.accept()  # 等到别人来  conn就类似于是双向通道
    print(addr)  # ('127.0.0.1', 51323) 客户端的地址
    while True:
        try:
            data = conn.recv(1024)
            print(data)  # b''  针对mac与linux 客户端异常退出之后 服务端不会报错 只会一直收b''
            if len(data) == 0:break
            conn.send(data.upper())
        except ConnectionResetError as e:
            print(e)
            break
    conn.close()

    客户端

import socket


client = socket.socket()
client.connect(('127.0.0.1',8080))

while True:
    msg = input('>>>:').encode('utf-8')
    if len(msg) == 0:continue
    client.send(msg)
    data = client.recv(1024)
    print(data)

  8.TCP粘包问题

    服务端

import socket


server = socket.socket()  # 买手机 不传参数默认用的就是TCP协议
server.bind(('127.0.0.1',8080))  # bind((host,port))  插电话卡  绑定ip和端口
server.listen(5)  # 开机    半连接池


conn, addr = server.accept()  # 接听电话  等着别人给你打电话     阻塞
data = conn.recv(5)  # 听别人说话 接收1024个字节数据          阻塞
print(data)
data = conn.recv(5)  # 听别人说话 接收1024个字节数据          阻塞
print(data)
data = conn.recv(4)  # 听别人说话 接收1024个字节数据          阻塞
print(data)

    客户端

import socket


client = socket.socket()  # 拿电话
client.connect(('127.0.0.1',8080))  # 拨号   写的是对方的ip和port

client.send(b'hello')
client.send(b'world')
client.send(b'baby')

    如何将数据打包成固定的包?

    运用struct模块

import struct
​
res = 'ssssssssssssssssssssssss'# 当原始数据特别大时,i模式打包不了,需要更换其他模式
res1 = struct.pack('i',len(res))
print(len(res1))  # 打包后的数据长度为4
​
res2 = struct.unpack('i',res1)[0]
print(res2)  # 解包之后的真实数据长度

    当数据量特别大的情况下该如何解决

import struct

# 当原始数据特别大的时候 i模式打包不了 需要更换模式?
# 如果遇到数据量特别大的情况 该如何解决?
d = {
    'name':'jason',
    'file_size':3455435435345354524534532456546546565466564366463654543453454353455,
    'info':'为大家的骄傲'
}
import json
json_d = json.dumps(d)
print(len(json_d))

res1 = struct.pack('i',len(json_d))
print(len(res1))

res2 = struct.unpack('i',res1)[0]
print('解包之后的',res2)

  9.解决粘包问题

    (1)发送数据直接先告诉对方数据量的大小

    (2)利用struct模块定制我们自己的消息传输协议

    服务端

      (1)先制作一个发送给客户端的字典

      (2)制作字典的报头

      (3)发送字典的报头

      (4)发送字典

      (5)再发真实数据

    客户端

      (1)先接受字典的报头

      (2)解析拿到字典的数据长度

      (3)接收字典

      (4)从字典中获取真实数据的长度

      (5)接收真实数据

import socket
import subprocess
import struct
import json


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)


while True:
    conn, addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if len(cmd) == 0:break
            cmd = cmd.decode('utf-8')
            obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            res = obj.stdout.read() + obj.stderr.read()
            d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'}
            json_d = json.dumps(d)
            # 1.先制作一个字典的报头
            header = struct.pack('i',len(json_d))
            # 2.发送字典报头
            conn.send(header)
            # 3.发送字典
            conn.send(json_d.encode('utf-8'))
            # 4.再发真实数据
            conn.send(res)
            # conn.send(obj.stdout.read())
            # conn.send(obj.stderr.read())
        except ConnectionResetError:
            break
    conn.close()

    客户端

import socket
import struct
import json

client = socket.socket()
client.connect(('127.0.0.1',8080))

while True:
    msg = input('>>>:').encode('utf-8')
    if len(msg) == 0:continue
    client.send(msg)
    # 1.先接受字典报头
    header_dict = client.recv(4)
    # 2.解析报头 获取字典的长度
    dict_size = struct.unpack('i',header_dict)[0]  # 解包的时候一定要加上索引0
    # 3.接收字典数据
    dict_bytes = client.recv(dict_size)
    dict_json = json.loads(dict_bytes.decode('utf-8'))
    # 4.从字典中获取信息
    print(dict_json)
    recv_size = 0
    real_data = b''
    while recv_size < dict_json.get('file_size'):  # real_size = 102400
        data = client.recv(1024)
        real_data += data
        recv_size += len(data)
    print(real_data.decode('gbk'))

  10.关于subproccess应用

import subprocess

cmd = input('cmd>>>:')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果

# subprocess获取到的数据 拿完就没有了  不能重复的拿
print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果

 

posted @ 2019-08-07 20:13  静心学  阅读(174)  评论(0)    收藏  举报