python

 socket 是什么?

  socket (又叫套接字)

  socket 是应用层与TCP/IP 协议簇通信的中间软件抽象层,它是一组接口。在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议簇封装在socket接口后面,对用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

  所以,我们无需深入理解tcp/ip协议,socket已经为我们封装好了,我么你只需要遵循socket的规定去编程,写出程序自然就遵循tcp/ip标准。

   

socket分类:

  基于文件类型的

  AF_UNIX

  unix下一切皆文件,基于文件的socket调用的就是底层的文件系统来取数据,两个套接字进程运行在泳衣机器,乐意通过访问同一个文件系统间接完成通信。

  基于网络的

  AF_INET

  还有AF_INET6,还有一些其他的地址家族。不过,他们要么是用于某平台,要么就是已经被废弃。AF_INET是使用最广泛的,python支持很多中地址家族,但是由于我们之关系年网络编程,所有大部分时间我们只用使用了AF_INET。

socket工作流程:

  

拓展知识:

  1.tcp三次握手和四次挥手

  2.SYN洪水攻击

基础示例:

server:(TCP)

import socket               # 导入 socket 模块

s = socket.socket()         # 创建 socket 对象
host = "127.0.0.1" # 获取本地主机名
port = 12345                # 设置端口
s.bind((host, port))        # 绑定端口

s.listen(5)                 # 等待客户端连接
while True:
    c, addr = s.accept()     # 建立客户端连接。
    print ('连接地址:', addr)
    info ="hello!"
    c.send(info.encode())
    c2 = c.recv(1024)
    print(c2)
    c.close()                # 关闭连接
s.close()

client:

#---------------
s = socket.socket()         # 创建 socket 对象
host = "127.0.0.1"  # 获取本地主机名
port = 12345                # 设置端口号

s.connect((host, port))
s2 = s.recv(1024).decode()
info = "nihao!"
s.send(info.encode())
print(s2,type(s2))
s.close()

进阶:

server端:(TCP方式)

import socket
re = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#地址重用的解决办法
re.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

re.bind(("127.0.0.1",12345))
re.listen()

while True:
    print("开始监听----")
    a, b = re.accept()
    print(b, "已接入链接....")
    try:
        while True:
            info = a.recv(1024)
            a.send(info)
        a.close()
    except ConnectionResetError as J:
        print("远程主机已断开......")

re.close()

client端:

import socket
con = socket.socket()
con.connect(("127.0.0.1",12345))
while True :
    info = input(">>>")
    con.send(info.encode())
    msc = con.recv(1024)
    print(msc.decode())
con.close()

server端:(utp方式)

import socket
ss = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

ip_port = ("127.0.0.1",8080)

ss.bind(ip_port)

while True:
    #元组:(data,IP_port)
    data,addr = ss.recvfrom(1024)
    print(data)
    ss.sendto(data.upper(),addr)

client端:

import socket

ip_port = ("127.0.0.1",8080)
cli = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    data = input(">>>:")
    cli.sendto(data.encode("UTF-8"),ip_port)
    data2,addr = cli.recvfrom(1024)
    print(data2)

TCP、UDP使用场景

TCP协议需要三次握手通信成功后进行建立连接.

  场景:互联网和企业网上的客户端应用、数据传输的性能必须让位于数据传输的完整性、可控制性和可靠性时,TCP协议是当然的选择。

UDP协议是直接发送,不会判断是否接收和发送成功.

  场景: 当强调传输性能而不是传输的完整性时,如:音频和多媒体应用,UDP是最好的选择。QQ使用的是UDP协议聊天,因为用户不需要等待就会接收。


粘包现象:

实例:(socket-TCP + subprocess)

server端:

import socket
import subprocess

ip_port = ("127.0.0.1",8080)
buffer_size = 1024

ss = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ss.bind(ip_port)
ss.listen(5)

while True:
    print("开始监听链接...")
    conn,addr = ss.accept()
    print(addr,"已链接....")
    while True:
        try:
            data = conn.recv(buffer_size)
            if data == "":continue
            data_de = data.decode('utf-8')
            data_msg = subprocess.Popen(data_de,shell=True,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE)
            if data_msg.stderr.read().__len__():
                data_send = "输入错误,请重新输入..".encode('gbk')
            else:
                data_send = data_msg.stdout.read()

            conn.send(data_send)

        except ConnectionResetError :
            print("远程主机已断开....")
            break
    conn.close()
ss.close()

client端:

import socket

ip_add = ("127.0.0.1",9999)
buff_size = 1024
se = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
se.connect(ip_add)

while True:
    data = input(">>>:")
    if data == "":continue
    se.send(data.encode("utf-8"))
    info = se.recv(1024)
    print(info.decode("gbk"))
se.close()

出现问题:

1.执行ipconfig  命令后 返回的数据并不完整.

2.执行第二条命令时,还是第一条命令的显示.

解决粘包方案:

server:

import socket
import subprocess
import json
import struct

#常量定义
ip = "127.0.0.1"
port = 9999
ip_port = (ip, port)
buffer_size = 1024

#socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(ip_port)
server_socket.listen(5)

while True:
    print("开始监听.....")
    conn, addr = server_socket.accept()
    print(addr, "已接入....")
    while True:
        try:
            #获取指令
            zl_b = conn.recv(buffer_size)
            print(zl_b)
            zl_s = zl_b.decode()
            
            #指令执行返回数据
            data = subprocess.Popen(zl_s, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            data_Y_b = data.stdout.read()
            data_E_b = data.stderr.read()
            
            #封装数据总长度
            data_len = len(data_Y_b) + len(data_E_b)
            data_dict = {"data_dict": data_len}
            json_data_s = json.dumps(data_dict)
            
            #封装head部长度
            json_data_b = json_data_s.encode()
            head_len_i = len(json_data_b)
            
            #封装前4个字节长度
            head_len_b = struct.pack("i", head_len_i)

            #发送4个字节,发送head,发送数据
            conn.send(head_len_b)
            print(len(head_len_b))
            conn.send(json_data_b)
            print(len(json_data_b))
            conn.send(data_Y_b)
            print(len(data_Y_b))
            conn.send(data_E_b)
            print(len(data_E_b))

        except Exception:
            break
    conn.close()
server_socket.close()

client:

import socket
import json
import struct

ip = "127.0.0.1"
port = 9999
ip_port = (ip,port)
buffer_size = 1024

client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client_socket.connect(ip_port)

while True:
    #发送指令至服务端.
    zhiling_s = input(">>>:").strip()
    if not zhiling_s:continue
    zhiling_b = zhiling_s.encode()
    client_socket.send(zhiling_b)

#层层封装,收取对应数据对应长度,解封装.

    #获取前四个字节长度
    head_len_b = client_socket.recv(4)
    head_len_i = struct.unpack('i',head_len_b)[0]
    print(head_len_i)

    #获取head部长度
    json_data_b = client_socket.recv(head_len_i)
    print(len(json_data_b))

    #获取数据总长度
    json_data_s = json_data_b.decode()
    data_dict = json.loads(json_data_s)
    data_len = data_dict["data_dict"]
    print(data_len)

    #循环收取总数据
    data_len_recv = 0
    data = b""
    while data_len_recv < data_len:
        data += client_socket.recv(buffer_size)
        data_len_recv += len(data)
    print(data.decode("gbk"))

client_socket.close()

问题遗留:

client 端收取数据
    data = struct.unpack("i",cli.recv(4))[0]
    data2 = iter(partial(cli.recv,data),b"").__next__().decode("gbk")

能否用两条命令就解决收取数据以及粘包问题?以上两条可以收取,依旧有粘包,待解决...

原文地址:https://www.cnblogs.com/Anec/p/9631656.html