zoukankan      html  css  js  c++  java
  • Socket接口

    Socket接口

    Socket: Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部。

    1.简单的套接字通信

    服务端

    import socket
    
    #1.买手机 创建一个套接字对象
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #基于网络通信的套接字 (TCP)
    # print(phone)
    
    #2.绑定手机卡(ip地址)
    phone.bind(('127.0.0.1',8080)) # 端口0-65535: 0-1024是给操作系统使用的
    
    #3.开机
    phone.listen(5) #最大挂起的链接数
    
    #4.等电话链接
    print('starting...')
    conn,client_addr=phone.accept() #conn 电话线 拿到可以收发信息的管道
    #accept 对应客户端的connect 三次握手
    
    #5.收,发消息
    data = conn.recv(1024) #1.单位:bytes 2. 1024代表接受1024个bytes
    print('客户端的数据',data)
    
    conn.send(data.upper())
    
    #6.挂电话
    conn.close()
    
    #7.关机
    phone.close()

    客户端

    import socket
    
    #1.买手机 客户端的phone 相当于服务端的conn
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    # print(phone)
    
    #2.拨号 (服务端的ip 和服务端的 端口)
    phone.connect(('127.0.0.1',8080)) #0-65535: 0-1024是给操作系统使用的
    
    #3.发收消息 bytes型
    phone.send('hello'.encode('utf-8'))
    data = phone.recv(1024)
    print(data)
    
    #4.关闭
    phone.close()

    2.加上通信循环

    服务端

    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.bind(('127.0.0.1',8081))
    phone.listen(5)
    
    print('starting...')
    conn,client_addr=phone.accept()
    print(client_addr)
    
    while True: #通信循环
        data = conn.recv(1024)
        print('客户端的数据',data)
        conn.send(data.upper())
    
    conn.close()
    phone.close()

    客户端

    import socket
    
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8081)) #0-65535: 0-1024是给操作系统使用的
    
    while True:
        msg = input('>>: ').strip()
        phone.send(msg.encode('utf-8'))
        data = phone.recv(1024)
        print(data)
    phone.close()

    3.C与S的bug修复

    BUG问题:

    1.端口的重复使用

    问题:重启服务端时可能会遇到

    这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址

    windows解决方法

    #服务端代码
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #SO_REUSEADDR 可以让ip端口重用
    phone.bind(('127.0.0.1',8080))

    linux解决方法

    发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
    vi /etc/sysctl.conf
    
    编辑文件,加入以下内容:
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30
     
    然后执行 /sbin/sysctl -p 让参数生效。
     
    net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    
    net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

    2.客户端关闭

    服务端

    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #SO_REUSEADDR 可以让ip 端口重用
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    
    print('starting...')
    conn,client_addr=phone.accept()
    print(client_addr)
    
    while True: #通信循环
        try:
            data = conn.recv(1024)
            # if not data:break #适用于linux操作系统
            print('客户端的数据',data)
            conn.send(data.upper())
        except ConnectionResetError:#使用于windows操作系统
            break
    
    conn.close()
    phone.close(

    客户端

    客户端应用程序在发送的时候:send==空-->客户端的操作系统内存 收到为空---> 不会调用任何协议,没有发送数据给,服务端的操作系统内存

    服务端应用程序在接受的时候:服务端的操作系统内存由于没有接收到数据,所以一直卡在,应用程序的recv接受过程

    import socket
    
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080)) #0-65535: 0-1024是给操作系统使用的
    
    while True:
        msg = input('>>: ').strip() # msg =''
        if not msg:continue
        phone.send(msg.encode('utf-8')) #phone.send(b'')
        # print('has send') #验证可以发空
        # >>:   has send
        data = phone.recv(1024)
        # print('has recv') #验证在发空的情况下 是否可以接受消息
        #>>:   has send
        print(data.decode('utf-8')) 
    
    phone.close()

    4.加上链接通信

    服务端一直对外进行服务:可以保证客户端一个一个链接进来

    链接新的客户端的条件:停止前一个客户端

    服务端

    import socket
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #SO_REUSEADDR 可以让ip 端口重用
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    
    print('starting...')
    while True: # 链接循环
        conn,client_addr=phone.accept()
        print(client_addr)
    
        while True: #通信循环
            try:
                data = conn.recv(1024)
                # if not data:break #适用于linux操作系统
                print('客户端的数据',data)
                conn.send(data.upper())
            except ConnectionResetError:#使用于windows操作系统
                break
        conn.close()
    
    phone.close()

    客户端

    import socket
    
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080)) #0-65535: 0-1024是给操作系统使用的
    
    while True:
        msg = input('>>: ').strip() # msg =''
        if not msg:continue
        phone.send(msg.encode('utf-8')) #phone.send(b'')
        # print('has send') #验证可以发空
        # >>:   has send
        data = phone.recv(1024)
        # print('has recv') #验证在发空的情况下 是否可以接受消息
        #>>:   has send
        print(data.decode('utf-8'))
    
    phone.close()

    客户端1

    import socket
    
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    phone.connect(('127.0.0.1',8080)) #0-65535: 0-1024是给操作系统使用的
    
    while True:
        msg = input('>>: ').strip() # msg =''
        if not msg:continue
        phone.send(msg.encode('utf-8')) #phone.send(b'')
        # print('has send') #验证可以发空
        # >>:   has send
        data = phone.recv(1024)
        # print('has recv') #验证在发空的情况下 是否可以接受消息
        #>>:   has send
        print(data.decode('utf-8'))
    
    phone.close()

    5.ssh远程执行命令

    windows命令:

    • dir: 查看某一文件夹下的子文件名与子文件夹名
    • ipconfig: 查看本地网卡的ip信息
    • tasklist: 查看运行的进程

    linux命令:

    • ls
    • ifconfig
    • pa aux

    问题:想要在服务端拿到命令,并且将命令的结果返回给客户端

    通过os.system可以执行系统命令,但是只能返回0或者1(命令正确 0 ,命令错误 1),无法返回出系统命令的执行结果

    import os
    res = os.system('dir E:')
    print('命令的结果: ', res)

    解决方法:

    创建管道:

    import subprocess
    obj = subprocess.Popen('dirsadas E:',shell=True,
                     stdout=subprocess.PIPE, #丢到管道里面shell是命令解释器的意思,启动一个程序来解析,前面的字符串,解析成相应的命令
                     stderr=subprocess.PIPE) 
    print(obj)
    # print('stout 1--->: ',obj.stdout.read().decode('gbk')) #第一次从管道中取走了数据
    # print('stout 2--->: ',obj.stdout.read().decode('gbk')) #第二次没有值所以为空
    
    print('stderr 1--->: ',obj.stderr.read().decode('gbk')) # 拿到错误管道的数据
    
    #在解码的时候,必须考虑到编码的格式,在管道里执行系统命令的是要提交给系统,
    # 所以判断出编码的格式是 windows默认的编码gbk

    服务端

    import socket
    import subprocess
    
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #SO_REUSEADDR 可以让ip 端口重用
    phone.bind(('127.0.0.1',8080))
    phone.listen(5)
    
    print('starting...')
    while True: # 链接循环
        conn,client_addr=phone.accept() #接受客户端链接
        print(client_addr)
    
        while True: #通信循环
            try:
                #1.收命令
                data = conn.recv(1024)
                print('客户端的数据',data)
    
                #2.执行命令,拿到结果
                obj = subprocess.Popen(data.decode('utf-8'), shell=True,
                                       stdout=subprocess.PIPE,  # 丢到管道里面
                                       stderr=subprocess.PIPE)  # shell是命令解释器的意思,启动一个程序来解析,前面的字符串,解析成相应的命令
    
                stdout = obj.stdout.read()
                stderr = obj.stderr.read()
    
                #3.把命令的结果返回给客户端
                conn.send(stdout+stderr) # '+'是可以优化的点
    
            except ConnectionResetError:# 使用于windows操作系统
                break
        conn.close()
    
    phone.close()

    客户端

    import socket
    
    phone=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
    
    phone.connect(('127.0.0.1',8080))
    while True:
        #1.发命令
        cmd = input('>>: ').strip() #dir C:
        if not cmd:continue
        phone.send(cmd.encode('utf-8'))
    
        #2.拿命令的结果,并打印
        data = phone.recv(1024) # 1024是一个坑
        print(data.decode('gbk')) #系统默认的编码
    
    phone.close()
  • 相关阅读:
    设计模式的类型
    SQL介绍(1)
    MySQL(介绍1)
    MyBatis总结(1)
    使用SQLServer Profiler侦测死锁(转)
    SQL Server 数据库中关于死锁的分析
    Delphi内嵌汇编语言BASM精要(转帖)
    Delphi项目构成之单元文件PAS
    Delphi中Interface接口的使用方法
    Delphi项目构成之项目文件DPR
  • 原文地址:https://www.cnblogs.com/Mryang123/p/8695815.html
Copyright © 2011-2022 走看看