zoukankan      html  css  js  c++  java
  • 用socktet实现简单的ssh

    首先谈一下什么是socket。socket的本质是API接口,是对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;如果说HTTP是轿车,提供了封装或者显示数据的具体形式,那么Socket是发动机,提供了网络通信的能力。

    而用socket实现简单的ssh,需要分析在客户端和服务器端的执行过程。

    客户端:

    第一步,指定协议类型,地址簇;

    第二步:链接服务端的地址;

    第三步:发送数据;

    第四步:接收数据;

    第五步:关闭客户端。

    服务器端:

    可将服务器端比作一个电话打进来的过程。

    第一步:指定协议类型,地址簇;

    第二步:绑定要监听的端口;

    第三步:进行监听,即对绑定的端口进行监听,看是否有“电话”打进来;

    第四步:等待电话打进来;

    第五步:接受数据;

    第六步:返回给客户端数据;

    第七步:关闭服务器。

    根据上述的过程,我们可写出下面的程序。

    客户端:

    import socket

    client = socket.socket()#第一步,指定协议类型,地址簇;

    '''

    在socket.socket内部有两个参数:socket.socket(family address,socket protocal type),

    family address,即地址簇,包括:

      AF.INTE : ipv4

      AF.INTE6 : ipv6

      AF.UNIX: local

    socket protocal type,即协议类型,包括:

      socket.SOCK_STREAM : tcp/ip

      socket.SOCK_DGRAM : udp

    不写默认地址簇ipv4,协议类型为tcp/ip

    '''

    client.connect(“localhost",9999)#第二步:连接端口,这表示链接本地的9999端口

    client.send("hello python!".encode("utf-8"))#第三步:发送数据:hello python!

    #send发送数据时,数据必须是byte类型

    client.recv(1024)#第四步:接收数据:1024个字节

    client.close()#第五步:关闭客户端

     服务器端:

    import socket

    server = socket.socket()#第一步,定义地址簇,协议类型。用法同上

    server.bind("localhost",9999)#第二步:绑定要监听的端口

    server.listen()#第三步:监听

    conn,addr = server.accept()#第四步:等待电话打进来

    #conn是客户端连过来在服务器端为其生成的一个链接实例

    #addr是客户端地址

    data = conn.recv(1024)#第五步:接受数据:1024个字节

    conn.send(data.upper())#第六步:发送数据,即接收字符串的大写

    server.close()#第七步:关闭服务器端

    上述代码只是简单的实现了客户端与服务器端的交互,其中还有一些欠缺的地方,如服务器只能与一个客户端进行交互,且只能与该客户端进行一次“通话”。下面的代码展示了客户端如何同服务器端进行多次交互。

    服务器端:

    import socket,os

    server = socket.socket()

    server.bind("localhost",9999)

    server.listen()

    while True:

    #外层循坏,监听服务端“电话”

      conn,addr = server.accept()

      while True:

      #内层循坏,实现服务器与客户端的多次通话

        data = conn.recv(1024)

        if not data:#客户端发送空数据则跳出外层

          print("client has lost...")

          break

        print("执行指令:",data)

        cmd_res = os.popen(data.decode("utf-8")).read()#执行在cmd上运行的结果

        #popen只支持字符串,且执行结果也是字符串

        if len(cmd_res) == 0:

          print("cmd has no output...")

        conn.send(str(len(cmd_res.encode("utf-8"))).encode("utf-8")#发送命令长度

        #使用len时应将字符串encode,因为中文字符在字符串中与在二进制中的len不同

        #在二进制中,中文的len是三个字节,而字符串中是一个字节

        conn.send(cmd_res.encode("utf-8")

    server.close()

    客户端:

    import socket

    client = socket.socket()

    client.connect("localhost",9999)

    while True:

    #实现发送多条命令

      cmd = input(">>:").strip()

      if len(cmd) == 0:continue#不能输入为空

      client.send(cmd.encode("utf-8")

      cmd_res_size = int(client.recv(1024))#接收命令长度

      cmd_res = ""

      while len(cmd_res.encode("utf-8"))<cmd_res.size:

      #send发送缓冲区的数据,而当数据过多时,客户端也只能接收1024个字节的数据。下一次客户端接收的仍是留于缓冲区内未发送的内容

      #避免出现接收内容不符情况,使用多次接收来接收客户端遗留在缓冲区的内容

        temp = client.recv(1024)

        cmd_res += temp.encode("utf-8")

      print(cmd_res)

    client.close()

    注意事项:

    1、在Linux上才能实现服务器与多个客户端通话,但通话的基础是在前一客户端断开的情况下才能进行。

    这是因为在Linux系统中,当客户端主动断开时,客户端将会接收到空消息,而在windows系统下,客户端断开服务器端将会直接报错。

    2、注意当使用send发送文件时,文件必须是byte类型,发送时需encode。接收时,文件也是byte类型的,要想查看字符型消息,需要decode。

    3、在Linux系统上,连续send两条内容很有可能发生粘包问题,即把两条send内容合并成一个发送,windows上也很有可能会出现粘包问题。解决的方法是在两个send之间可以接收

     一条来自客户端的数据,即recv一个数据,即可解决粘包问题。

  • 相关阅读:
    mysql表单输入数据出现中文乱码解决方法
    swift实现水仙花数
    Mac终端使用swift REPL异常处理方法
    灰度发布、金丝雀发布,持续集成
    CentOS7.2配置本地yum源
    SNMP 相关检测分支
    Flask 中 @property 和@password.setter 的运用
    Python 数据结构--字典
    Python 数据结构--序列
    网络编程
  • 原文地址:https://www.cnblogs.com/liumuz/p/8675379.html
Copyright © 2011-2022 走看看