scoket套接字

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口

 

 

 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

 

 scoket模块

import socket
# 生成对象
server = socket.socket()
# 绑定ip和端口
server.bind(('127.0.0.1',8080))
#绑定参数为一个元组
#127.0.0.1这叫回送地址  表示电脑本身   端口 为普通整数 一般为8000以上
# 监听  半连接池
server.listen(5)
# 接收链接请求
# 第一个是表示客户端的socket    第二个客户端的地址信息
conn,addr = server.accept()
#接收字节
data = conn.recv(1024)
print(data)
# 发送数据
conn.send(b'hello  world')
# 关闭
conn.close()
# 关机
server.close()
服务端
import socket

client = socket.socket()
# 连接服务端
client.connect(('127.0.0.1',8080))
#发送数据
client.send(b'ni hao')
# 接收数据
data = client.recv(1024)

print(data)
# 关闭
client.close()
客户端

 

accept  是一个阻塞函数  会一直等到有客户端链接过来 在继续执行
connect本质实在进行三次握手   也是一个数据传输的过程  如果服务器没有立马响应 也会阻塞

 send

发送数据 本质上是把数据交给操作系统来进行发送 一旦数据交给了操作系统 后续的发送

应用程序就无法控制了 ,send一般不会卡 当然 如果数据量很大就会阻塞

scoket套接字是从操作系统缓存区读取数据   
如果当前还没有任何数据, 就会进入阻塞会一直等到有数据到来 再继续执行

  

send与recv对应,不要出现两边都是相同的情况

  

异常

# 如果已经开启了服务器  再次运行将会抛出 端口占用异常  把之前开启的服务器关掉即可
1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致!

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

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

 升级版

import socket
# 产生对象
server = socket.socket()
# 绑定ip和端口
server.bind(('127.0.0.1',8080))
# 监听
server.listen(5)
while True:
    # 接收
    conn,addr = server.accept()
    # print(conn)
    # print(addr)
    # 接收
    while True:
        try:
            data = conn.recv(1024)
            print(data)
            # 防止客户端发生异常或断掉
            if len(data) == 0:
                break
            conn.send(data.upper())
        except ConnectionResetError as a:
            print(a)
            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)
客户端

TCP粘包问题

TCP流式协议, 数据之间没有分界, 就像水  一杯水和一杯牛奶倒在一起了!

UDP 用户数据报协议    

粘包 仅发生在TCP协议中   

1. 发送端 发送的数据量小 并且间隔短 会粘

2. 接收端 一次性读取了两次数据的内容    会粘 
3. 接收端 没有接收完整  剩余的内容 和下次发送的粘在一起

无论是那种情况,其根本原因在于  接收端不知道数据到底有多少 

代码

import socket

server = socket.socket()

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

conn ,addr = server.accept()
data = conn.recv(1024)
print(data)
data = conn.recv(10)
print(data)
data = conn.recv(7)
print(data)
data = conn.recv(20)
print(data)
        
服务器
import socket

client = socket.socket()
client.connect(('127.0.0.1',8080))
client.send(b'strawberry')
client.send(b'berries')
client.send(b'interconnection')
客户端
# TCP特点
# 会将数据量比较小的并且时间间隔比较短的数据
# 不知到对方要传的数据有多大
# 将数剧打包成固定长度 发送给对方

例子:

import socket

import subprocess

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

# 敲入命令行拟cmd实行命令
while True:
    conn,addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if not len(cmd):
                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()
            print(len(res))
            conn.send(res)
        except ConnectionResetError as a:
            print(a)
            break
    conn.close()
服务端
import socket
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
    cmd = input('>>>').encode('utf-8')
    if not len(cmd):
        continue
    client.send(cmd)
    data = client.recv(1024)
    print(data.decode('gbk'))
客户端

 

解决粘包问题
	服务端
		1.先制作一个发送给客户端的字典
		2.制作字典的报头
		3.发送字典的报头
		4.发送字典
		5.再发真实数据
	
	客户端
		1.先接受字典的报头
		2.解析拿到字典的数据长度
		3.接受字典
		4.从字典中获取真实数据的长度
		5.接受真实数据
		

如:

import socket
import struct
import json
import subprocess

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 not len(cmd):
                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':'baobo','file_size':len(res),'info':'asdfghjwertyuwertzxcvbnmdfghjkejhk'}
            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)
        except ConnectionResetError as a:
            print(a)
            break
    conn.close()
服务端

 

import socket
import struct
import json


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

while True:
    cmd = input('>>>').encode('utf-8')
    if not len(cmd):
        continue
    client.send(cmd)
#     1.先接受字典报头
    header_dict = client.recv(4)
    # 2.解析报头 获取字典的长度
    dict_size = struct.unpack('i',header_dict)[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'):
        data = client.recv(1024)
        real_data += data
        recv_size += len(data)
    print(real_data.decode('gbk'))
客户端

 

struct模块

 struct pack//unpack
python中的struct主要是用来处理C结构数据的,读入时先转换为Python的字符串类型,然后再转换为Python的结构化类型,比如元组(tuple)啥的~。一般输入的渠道来源于文件或者网络的二进制流。
struct.pack()和struct.unpack()
在转化过程中,主要用到了一个格式化字符串(format strings),用来规定转化的方法和格式。
struct.pack(fmt,v1,v2,.....)
将v1,v2等参数的值进行一层包装,包装的方法由fmt指定。被包装的参数必须严格符合fmt。最后返回一个包装后的字符串。
struct.unpack(fmt,string)
解包。比如pack打包,然后就可以用unpack解包了。返回一个由解包数据(string)得到的一个元组(tuple), 即使仅有一个数据也会被解包成元组。

 

posted on 2019-08-07 22:39  我只想你  阅读(161)  评论(0编辑  收藏  举报