操作系统(简称OS)基础:

应用软件不能直接操作硬件,能直接操作硬件的只有操作系统;所以,应用软件可以通过操作系统来间接操作硬件

网络基础之网络协议:

网络通讯原理:

  连接两台计算机之间的Internet实际上就是一系列统一的标准,这些标准称之为互联网协议;互联网的本质就是一系列的协议,总称为“互联网协议” (Internet Protocol Suite)

  互联网协议的功能:定义计算机何如接入Internet,以及接入Internet的计算机通信的标准。

  osi七层协议: 互联网协议按照功能不同分为OSI七层或TCP/IP五层或TCP/IP四层

用户感知到的只是最上面的一层应用层,自上而下每层都依赖于下一层;每层都运行特定的协议,越往上越靠近用户,越往下越靠近硬件

  物理层功能:主要是基于电气特性发送高低电压(电信号),高电压对应的数字为1,低电压对应数字0

  数据链路层:

    数据链路层的由来: 单纯的电信号0、1没有任何意义,必须要规定电信号多少位一组,每组什么意思

    数据链路层的功能:定义了电信号的分组方式

    以太网协议(Ethernet):Ethernet协议规定了:1. 一组电信号构成一个数据包,叫做“帧”;2. 每一数据帧分成“报头”head和数据data两部分

       head包含(固定18个字节):1. 发送者/原地址,6个字节; 2. 数据类型,6个字节; 3. 接受者/目标地址,6个字节

       data包含:数据包的具体内容

    Mac地址:head中包含的源、目标地址的由来:Ethernet规定接入Internet的设备都必须具备网卡,发送端和接收端的地址便是指网卡的地址,即Mac地址;

     (每块网卡出厂时都被烧制上一个世界唯一的Mac地址,长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号))

    广播: 有了Mac地址,同一网络内的两台主机就可以通信了;Ethernet采用最原始的广播的方式进行通信,即计算机通信基本靠吼

  网络层:有了Ethernet、Mac地址、广播的发送方式,同一个局域网内的计算机就可以彼此通讯了,但世界范围内的互联网是由一个个彼此隔离的小的局域网(子网)组成的,所以不能所有的通信都采用以太网的广播方式

    从上图可以看出:必须找出一种方法来区分哪些计算机属于同一广播域、哪些不是,如果是就采用广播的方式发送;如果不是就采用路由的方式(向不同广播域/子网分发数据包),Mac地址是无法区分的,它只跟厂商有关

    网络层功能:引入一套新的地址来区分不同的广播域(子网),这套地址即网络地址

    IP协议:1. 规定网络地址的协议叫IP协议,它定义的地址称为IP地址,广泛采用的v4版本即ipv4,它规定网咯地址由32位2进制表示;

        2. 范围0.0.0.0-255.255.255.255

        3. 一个IP地址通常写成四段十进制数,例如:172.16.10.1

      IP地址分成两部分: 1. 网络部分:标识子网; 2. 主机部分:标识主机

        注:单纯的IP地址段只是标识了IP地址的种类,从网络部分或主机部分都无法辨识一个IP所处的子网

      子网掩码:表示子网络特征的一个参数;知道了“子网掩码”,我们就能判断任意两个IP地址是否处在同一个子网络。

      网络层作用总结:IP协议的主要作用有两个:1. 为每台计算机分配IP地址;2.确定哪些地址在同一个子网络

    IP数据包:分为head和data两个部分,然后直接放入以太包的data部分,如下所示:

    ARP协议:由来:计算机通信基本靠吼,即广播的方式,所有上层的包到最后都要封装上以太网头,然后通过以太网协议发送;通信是基于Mac的广播方式实现,计算机在发包时获取自身的Mac容易,如何获取目标主机的Mac就需要通过ARP协议。

          ARP协议功能:广播的方式发送数据包,获取目标主机的Mac地址

         协议工作方式: 每台主机IP都是已知的

           1. 首先通过IP地址和子网掩码区分出自己所处的子网

           2. 分析是否处于同一网络(如果不是同一网络。通过ARP获取的是网关的Mac)

          

           3. 这个包以广播的方式在发送端所处的子网内传输,所有主机接收后拆开包,发现目标IP是自己的就响应返回自己的Mac(这点还不是很理解,发送端所处的子网??)

    传输层:由来:网络层的IP帮我们区分子网,以太网层的Mac帮我们找到主机,然后大家使用的都是应用程序,那么我们通过IP和Mac找到了一台特定的主机;然后,标识这台主机上的应用程序就是端口,端口即应用程序和网卡关联的编号。

        传输层功能:建立端口到端口的通信

        补充:端口范围0-65535,0-1023为操作系统占用端口

        TCP协议: 可靠传输,需要挖双向“通道“,””3次“握手”和4次“挥手”;流式协议

        UDP协议:不可靠传输,不需要挖“通道”;又称“数据报协议”

    

    应用层:由来:用户使用的都是应用程序,均工作于应用层,互联网是开发的,大家都可以开发自己的应用程序,数据多种多样,必须规定好数据的组织形式

        应用层功能: 规定应用程序的数据格式

          例如: TCP协议可以为各种各样的程序传递数据,比如Email、www、FTP等;那么必须有不同协议规定电子邮件、网页、发图片数据的格式,这些应用程序协议就构成了“应用层”

          

Socket:

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

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

附:也有人将socket说成IP+port,IP是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,IP地址是配置到网卡上的,而port是应用程序开启的,IP与port的绑定就标识了互联网中独一无二的一个应用程序; 而程序的pid是同一台机器上不同进程或线程的标识

套接字: 套接字有两种(或者说两个种族),分别是基于文件型的和基于网络型的。

基于文件类型的套接字家族: 套接字家族的名字是 : AF_UNIX

基于网络类型的套接字家族: 套接字家族的名字是:AF_INET  

  还有AF_INET6被用于ivp6;AF_INET是使用最广泛的一个,python支持很多地址家族,但是由于我们只关心网络编程,所以大部分时候我们只是用AF_INET

套接字(socket) 工作流程:以打电话为例说明:

客户端代码如下:

import socket

# 1. 买“手机”
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print(phone)
# 打印结果:
# <socket.socket fd=300, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> # 2. “拨号” (客户端不需要绑定IP和端口)
phone.connect(("127.0.0.1",8080))
"""
# connect 发起连接, ("127.0.0.1",8080)是服务端的IP和端口;
# 客户端的connect对应服务端的accept(),connect()和服务端的accept()底层进行的就是TCP的“三次握手”
# 服务端accept()之后,客户端的phone就相当于服务端的那个 conn,就是那根“电话线”
"""
print(phone)
# 运行结果: # 服务端accept()之后客户端的phone就发生了变化,变得和服务端中的conn对应
# <socket.socket fd=300, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 62064), raddr=('127.0.0.1', 8080)> # 发、收消息;发收的消息都是bytes类型
phone.send("hello".encode("utf-8")) # 发
"""
# 不能直接发字符串, 物理层传输的0101,这一步需要发送bytes类型;
# 字符串转bytes: string.encode(编码格式)
# phone.send() 对应服务端的 conn.recv()
"""
data = phone.recv(1024) # 收
print(data) # 关闭
phone.close() # 先启动服务端,再启动客户端,运行结果如下:
# b'HELLO'

服务端代码如下:

import socket

# 1. 买“手机”
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 基于网络通讯的、基于TCP协议的套接字;phone就是一个套接字对象 # 这一步得到一个服务端的套接字phone
"""
全称是: phone = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
socket.socket() # socket下面的socket类;
family=socket.AF_INET # 地址家族(socket的类型)是基于网络通讯的AF_INET
type=socket.SOCK_STREAM # 用的是流式的协议,即 TCP协议
"""
# print(phone)
# 打印结果
# <socket.socket fd=316, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> # 2. 绑定“手机卡”(服务端的IP地址需要固定下来、并公示给别人;客户端虽然有IP和端口但是不需要绑定)
phone.bind(("127.0.0.1",8080))
"""
# 服务端需要绑定IP和Port(IP和端口),ip和端口需要以元祖的形式传进来;
其中第一个参数是字符串形式的IP地址; 127.0.0.1是指本机,专门用于测试的,IP写成这个就意味着服务端和客户端都必须在同一台主机上;
第二个参数是端口;端口范围是0-65535,其中0-1023是给操作系统使用的,2014以后的你可以使用
""" # 3. “开机”
phone.listen(5) # 开始TCP监听
"""
# 5代表最大挂起的链接数; 通常这个数写在配置文件中
# Enable a server to accept connections. If backlog is specified, it must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections. If not specified, a default reasonable value is chosen.
""" # 4. 等电话
# res = phone.accept()
# print(res)
# print(phone)
"""
# 等待链接; 等待的结果赋值给一个变量
# 服务端程序启动后,程序会停在这一步;
# 服务端的accept()对应客户端的connect()
# accept()底层建立的就是TCP的“三次握手”,“三次握手”之后会建成一个双向的链接(下面的conn),然后客户端得到一个对象(新的phone)、服务端得到一个对象(conn),这两个对象都可以收、发消息
"""
# 客户端的程序启动后,服务端的程序也从 res = phone.accept()这一步接着往下运行
# 其中一次的运行结果:
# (<socket.socket fd=356, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 56572)>, ('127.0.0.1', 56572))
# 元祖的形式,元祖里面有2个元素,第一个元素是发送端的链接对象(套接字对象),第二个元素是客户端的IP和端口 # <socket.socket fd=312, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080)> """
由于phone.accept()得到的结果是元祖的形式,里面有两个元素:第一个、客户端的链接对象(相当于拨号人的电话线);第二个、客户端的IP和端口,所以phone.accept()可以写成如下形式
""" conn,client_addr = phone.accept()
print(conn)
# 打印结果:
# <socket.socket fd=328, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 62064)> # 5. 收、发消息(基于刚刚建好的那根“电话线”(conn)收发消息),收发的消息都是bytes类型
data = conn.recv(1024) # 收
print("客户端的数据",data)
"""
# conn.recv(1024):接收conn这个发送端对象发来的数据(或者理解成沿着conn这根“电话线”接收消息)
# 括号内的数字需要注意两个地方: 1. 数字单位:bytes;2. 数字2014代表最大接收1024个bytes
# conn.recv(1024)接收到的数据赋值给变量 data
""" conn.send(data.upper()) # 发
"""
# conn.send(data.upper()):给conn的客户端发送消息(沿着conn这个“电话线”发送消息)
# .upper() # 把字符串里面的都变成大写
""" # 挂电话(关闭)
conn.close() # 关机
phone.close() # 运行结果:
# 客户端的数据 b'hello' """
1. 服务端有两种套接字对象:服务端的phone和conn
服务端的phone用于:绑定(IP和端口)、监听TCP和最重要的接收接收客户端的链接和客户端的IP、端口
conn用于收发消息
2. 客户端有一种套接字对象:客户端的phone(其实客户端的phone在服务端accept之后也发生了变化),它的作用是:发起建链接请求(.connect())和发、收消息
"""

简单套接字加上通信循环:

把上面的代码加上 while True 就变成了循环通信,如下所示

客户端代码:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(("127.0.0.1",8080))
while True:
msg = input(">>>").strip()
phone.send(msg.encode("utf-8"))
data = phone.recv(1024)
  
print(data) # 也是bytes形式
   """
   如果想要打印正常的形式,可利用利用:
   print(data.decode("utf-8))
   """
phone.close()

服务端代码:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind(("127.0.0.1",8080))
phone.listen(5) conn,client_addr = phone.accept()
print(client_addr) while True:
data = conn.recv(1024) # 在收到消息之前,程序也“卡”在这一步; 所以,recv()的具体含义是“等待接收消息”
print("客户端的数据",data)
conn.send(data.upper()) conn.close() phone.close()

重启服务端的时候可能出现端口仍然被占用的情况,原因是端口被操作系统回收需要时间,解决办法如下:

为服务端加一句代码:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 在绑定之前加上这句代码; # reuseaddr表示重新用该端口
phone.bind(("127.0.0.1",8081))
phone.listen(5) conn,client_addr = phone.accept()
data = conn.recv(1024)
print("客户端的数据",data)
conn.send(data.upper())
conn.close()
print(phone)
phone.close()

客户端和服务端代码bug修复:

客户端可以发空消息,但服务端却收不到空消息,如下代码:

客户端:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(("127.0.0.1",8080))
while True:
msg = input(">>>").strip()
"""
客户端可以发空数据,但是服务端却收不到空数据
解决客户端发空消息可以用如下代码:
"""
if not msg:continue # 如果发的消息为空,则重新发
phone.send(msg.encode("utf-8"))
data = phone.recv(1024)
print(data.decode("utf-8"))    """
   recv和send都是python(应该说是应用程序)发给操作系统的命令
   收发消息需要通过Internet进行传输,而Internet需要通过网卡去发送、接收数据,只有操作系统才能调用网卡这个硬件
   所以,具体执行发送、接收消息动作的是操作系统(就如文件处理中的open(file)一样),python(应用程序)把发送的消息的内存原封不动地复制给操作系统,然后操作系统去发送消息;    当客户端发送空消息时,应用程序会把这个空消息复制给操作系统,正常情况下操作系统会根据TCP协议调用网卡,但由于操作系统收到的是空消息,所以操作系统没有调用任何硬件,
   也就是说,python(应用程序)发送的空消息只发到了客户端操作系统这一步,然后客户端的操作系统并没有接着往下发这个空消息;所以客户端的程序就卡在了这一步
   """
phone.close()

服务端:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(("127.0.0.1",8080))
phone.listen(5) conn,client_addr = phone.accept()
print(client_addr) while True:
data = conn.recv(1024)
print("客户端的数据",data)
conn.send(data.upper()) conn.close() phone.close()

还有一种情况:以上面的代码为例, 由于conn是基于客户端和服务端建立起来的一个双向通道,假如客户端被强行终止掉了,那么这个双向通道conn就没有意义了;在Windows系统下,假如客户端被强行终止,那么服务端就会报错,但在Linux系统下,服务端不会报错,而是进入了while的死循环,为了防止Linux的这个死循环,可以利用如下方法解决:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(("127.0.0.1",8080))
phone.listen(5) conn,client_addr = phone.accept()
print(client_addr) while True:
data = conn.recv(1024)
if not data:break
"""
由上面的分析可知:正常情况下服务端不可能收到空消息,因为假如客户端发了空消息,那么客户端的操作系统根本不会把这个空消息发出去;
所以,假如data变成了空消息,那一定是因为conn这个双向通道少了一方,也就是客户端单方面终止了;
所以 if not data:break # 就是说,假如客户端已经终止了,那就结束服务端的这个while True循环
"""
print("客户端的数据",data)
conn.send(data.upper()) conn.close() phone.close()

上述方法是针对Linux的;Windows下客户端当方面终止程序,服务端直接报错,所以应该用 try...except...去解决:

while True:
try:
data = conn.recv(1024) print("客户端的数据",data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
phone.close()

服务端为多个客户端提供服务:

服务端代码如下:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(("127.0.0.1",8080))
phone.listen(5)
"""
这个服务端可以为多个服务端服务,但同一时间只能服务于一个客户端;
当有其他服务端发来建链接请求时就挂起,当正在被服务的服务端退出后,挂起的其他服务端建链接的请求就会执行;
5为最大的挂起链接数
""" while True: # 链接循环
conn,client_addr = phone.accept()
print(client_addr) while True: # 通讯循环
try:
data = conn.recv(1024)
print("客户端的数据",data)
conn.send(data.upper())
except ConnectionResetError:
break conn.close()
phone.close()

模拟ssh远程执行命令:

关于系统命令的知识点补充:

# 一、系统命令:

# windows:
# dir # 查看某个文件夹下的子文件名和子文件夹名
# ipconfig # 查看本地网卡的IP信息
# tasklist # 查看运行的进程 # Linux系统对应的是:
# ls
# ifconfig
# ps aux """
系统命令不能直接在pycharm上写,而应该在cmd上输入(Windows系统);
cmd也是一个程序,它的功能非常单一,就是来接收你输入的有特殊意义的单词(命令),然后把你输入的有特殊意义的单词(命令)解析成操作系统认识的指令去执行;所以这个程序称之为“命令解释器”
如: dir f;\learning
Linux系统中: / 代表c盘
""" # 二、执行系统命令:
# 1、考虑使用os模块
# import os
# os.system("dir f;\learning") # 字符串形式的命令 # 但是这种方法是在服务端的终端上打印了dir f:\learning 的子文件和子文件夹名;而我们想要的结果是把命令结果拿到客户端然后再客户端打印 """
res = os.system("dir f;\learning") # res 只是 os.system("dir f:\learning") 的执行状态结果:0或者非0(0代表命令执行成功),并不是命令的查看结果
""" # 执行系统命令,并拿到命令的结果
# 2. subprocess模块的Popen
import subprocess
obj = subprocess.Popen("dir f:\learning",shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) # 命令的结果赋值给obj
"""
# 第一个参数是字符串格式的命令
要写: shell = True # shell是指命令解释器 # 启动一个程序来解析前面的字符串,把这个字符串解析成相应的命令去执行 # 相当于起了一个cmd
这个事例中,不管执行结果正确与否,命令的结果都只有一个,你没告诉subprocess把命令的结果给谁,它就把结果默认给了终端;但我们想要的是把命令的结果给客户端,而不是终端
所以我们需要通过某种手段告诉subprocess不要把结果给终端,而是把结果先存到一个地方,等我调用的时候发送给客户端,所以就用到了“管道”的概念;
把命令的结果放到一个管道里面(操作系统的内存),等你需要的时候再去管道里面取
让subprocess把结果放到管道里的方法:
stdout = subprocess.PIPE # stdout是命令的正确执行结果 # 命令的正确执行结果放到一个管道里面
stderr = subprocess.PIPE # stderr是命令的错误执行结果 # 每次的 .PIPE都触发一次PIPE的功能,从而产生一个新的管道;so 这两个 PIPE是不一样的
""" print(obj)
# 打印结果:
# <subprocess.Popen object at 0x0000007994CEA8D0> print("stdout---->",obj.stdout.read()) # obj从stdout(正确结果)这个管道里面读 (从管道读取一次之后再取就没有了) # 打印结果:(bytes格式)(不管服务端还是客户端,收、发消息都得是bytes形式)
# stdout----> b' \xc7\xfd\xb6\xaf\xc6\xf7 F \xd6\xd0\xb5\xc4\xbe\xed\xc3\xbb\xd3\xd0\xb1\xea\xc7\xa9\xa1\xa3\r\n \xbe\xed\xb5\xc4\xd0\xf2\xc1\xd0\xba\xc5\xca\xc7 BCA5-0E10\r\n\r\n f:\\learning \xb5\xc4\xc4\xbf\xc2\xbc\r\n\r\n2018/01/17 16:19 <DIR> .\r\n2018/01/17 16:19 <DIR> ..\r\n2018/01/12 01:04 <DIR> funny\r\n2018/01/15 14:12 <DIR> IDLE\xd7\xf7\xd2\xb5\xb2\xe2\xca\xd4\r\n2018/02/05 12:04 <DIR> pycharm_pro\r\n2018/01/19 09:16 <DIR> pythontest\r\n2018/03/10 11:05 <DIR> \xd7\xf7\xd2\xb5\xcc\xe1\xbd\xbb\r\n2018/03/08 11:45 <DIR> \xb2\xa9\xbf\xcd\xa1\xa2\xb4\xed\xce\xf3\xa1\xa2\xd2\xc9\xce\xca\xbd\xd8\xcd\xbc\r\n2018/01/27 18:01 <DIR> \xbd\xd8\xcd\xbc\r\n2018/01/16 10:33 <DIR> \xd7\xd4\xd1\xa7\r\n2018/01/11 14:53 <DIR> \xc4\xac\xd0\xb4\r\n 0 \xb8\xf6\xce\xc4\xbc\xfe 0 \xd7\xd6\xbd\xda\r\n 11 \xb8\xf6\xc4\xbf\xc2\xbc 115,108,585,472 \xbf\xc9\xd3\xc3\xd7\xd6\xbd\xda\r\n' # obj.stdout.read()是bytes格式,如果想看bytes格式里面具体是什么内容,则需要 decode();
print("stdout---->",obj.stdout.read().decode("gbk"))
"""
encode()是按照什么编码,decode()也需要按照相应的编码;
subprocess.Popen("dir f;\learning")执行的是系统命令,这个命令是提交给操作系统的,由操作系统执行完后拿到一个结果;
由于没告诉操作系统命令的结果用什么格式编码,所以系统会用它默认的编码格式;所以:
obj.stdout.read().decode("gbk")
""" print("stderr--->",obj.stderr.read().decode("gbk")) # 执行结果不一定正确,所以也要从obj.stderr 读取 # 打印结果:
# stdout---->
# stderr--->

模拟ssh远程执行命令具体代码:

客户端:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(("127.0.0.1",8080))
while True:
# 1. 发命令
cmd = input(">>>").strip() # 客户端在这行代码输入一条命令
if not cmd:continue
phone.send(cmd.encode("utf-8")) # 2. 得到命令的结果,并打印
data = phone.recv(1024) # data是bytes格式,打印需要解码 # 1024是个坑,待优化
print(data.decode("gbk"))
"""
data 解码需要是gbk,因为:data是由服务端传来的(stdout+stderr),而stdout和stderr是由 subprocess.Popen()得到的
subprocess.Popen("命令")是把命令交给了操作系统去处理,操作系统处理命令后会按照自己默认的编码把处理结果encode,而Windows的默认编码是 gbk
""" phone.close()

服务端:

import subprocess
import socket phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(("127.0.0.1",8080))
phone.listen(5) while True: # 链接循环
conn,client_addr = phone.accept() while True: # 通讯循环
try:
# 1. 接收命令
cmd = conn.recv(1024) # cmd是bytes格式 # 2. 执行命令,拿到执行后的结果
obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) # subprocess.Popen()中需要的是字符串格式的命令,所以需要把cmd decode;由于客户端是按照utf-8进行的encode,所以这步需要decode("utf-8")
stdout = obj.stdout.read() # obj.stdout需要read
stderr = obj.stderr.read() # stderr和stdout都是bytes格式的 # 3. 把命令的结果返回给客户端
conn.send(stdout+stderr) # + 会影响效率;因为 + 是重新创建了一份stdout和stderr的新的内存空间(把stdout和stderr的内存空间重新copy了一遍)# 所以+是一个可以优化的点
except ConnectionResetError:
break conn.close()
phone.close()

网络编程基础:网络基础之网络协议、socket模块的更多相关文章

  1. [C#网络编程系列]专题一:网络协议简介

    转自:http://www.cnblogs.com/zhili/archive/2012/08/11/NetWorkProgramming.html 因为这段时间都在研究C#网络编程的一些知识, 所以 ...

  2. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

  3. 《Unix 网络编程》15:Unix 域协议

    Unix 域协议 ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ 本 ...

  4. 脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    1.引言 本文接上篇<脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手>,继续脑残式的网络编程知识学习 ^_^. 套接字socket是大多数程序员都非常熟悉的概念,它是计算机 ...

  5. [转帖]脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?

    脑残式网络编程入门(二):我们在读写Socket时,究竟在读写什么?     http://www.52im.net/thread-1732-1-1.html   1.引言 本文接上篇<脑残式网 ...

  6. 【TCP/IP网络编程】:01理解网络编程和套接字

    1.网络编程和套接字 网络编程与C语言中的printf函数和scanf函数以及文件的输入输出类似,本质上也是一种基于I/O的编程方法.之所以这么说,是因为网络编程大多是基于套接字(socket,网络数 ...

  7. python基础(29):网络编程(软件开发架构、网络基础、套接字初使用)

    1. 软件开发架构 我们了解的程序之间通讯的应用可分为两种: 第一种是应用类:qq.微信.百度网盘.腾讯视频这一类是属于需要安装的桌面应用. 第二种是web类:比如百度.知乎.博客园等使用浏览器访问就 ...

  8. 总结day23 ---- 网络编程,以及计算机基础概念

    计算机网络的发展及基础网络概念 问题:网络到底是什么?计算机之间是如何通信的? 早期 : 联机 以太网 : 局域网与交换机 广播 主机之间“一对所有”的通讯模式,网络对其中每一台主机发出的信号都进行无 ...

  9. 网络编程入坑基础-BIO总结

    IO总结 前提 参考资料: <Java I/O> -- 这本书没有翻译版,需要自己啃一下. <Java I/O>这本书主要介绍了IO和NIO的相关API使用,但是NIO部分并不 ...

  10. 网络编程中TCP基础巩固以及Linux打开的文件过多文件句柄的总结

    1.TCP连接(短链接和长连接) 什么是TCP连接?TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议. 当网络通信 ...

随机推荐

  1. HttpURLConnection教程

    1.Class Overview An URLConnection for HTTP (RFC 2616) used to send and receive data over the web. Da ...

  2. SpringCloud开发学习总结(一)—— 基础知识

    1:Dubbo和Spring Cloud的关系 就我个人对这两个框架的使用经验和理解,打个不恰当的比喻:使用Dubbo构建的微服务架构就像组装电脑,各环节我们的选择自由度很高,但是最终结果很有可能因为 ...

  3. Android开发学习——ButterKnife使用

    为了码代码的效率,我们有了ButterKnife;其基本使用如下步骤: 1.在Android Studio的Setting中,下载plugin 2.在整个Project的build.gradle中添加 ...

  4. String的用法——转换功能

    package cn.itcast_05; /* String类的转换功能: byte[] getByte():把字符串转换成字节数组 复习: public String(byte[] bytes): ...

  5. SpringIOC学习_属性注入(依赖注入)

    一.应用场景:Spring会帮创建实现类的实例,但是有时候我们还需要在类中设置一些属性用于传入设置值,这些跟类紧密关联的属性就叫依赖,通过spring帮忙设置的过程叫依赖注入. 二.依赖注入的实现 A ...

  6. CSS3 按钮特效(一)

    1. 实例 2.HTML 代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  7. Godaddy域名301跳转问题处理

    前言:Godaddy的域名301跳转一共有六步,详情见以下步骤: 第一步: 第二步:找到你的域名,并点击DNS 第三步:点击添加 第四步:添加解析ip地址 第五步:域名转址,也就是301跳转 第六步: ...

  8. elasticsearch.yml 配置说明

    cluster.name: 指定node所属的cluster. node.name: 本机的hostname. node.master: 是否可以被选举为master节点.(true or false ...

  9. 核武器代理CC工具V3.42最新版本!

    软件说明 !!!有新版本更新,请移步到更新地址:https://www.cnblogs.com/cnhacker/p/10878688.html ########################### ...

  10. jquery 微信端 点击物理返回按钮,弹出提示框

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...