python 第七天

1、socket就是将下边的TCP/IP协议进行封装,留出来的接口用户通信

1.1、Socket Families

socket.AF_UNIX unix本机进程间通信

socket.AF_INET IPV4

socket.AF_INET6 IPV6

1.2、Socket Types

socket.SOCK_STREAM   #for tcp

socket.SOCK_DGRAM   #for udp

2、Socket 参数

2.1、socket.socket(family=AF_INETtype=SOCK_STREAMproto=0fileno=None)

声明socket类型,同时生成socket链接对象

2.2、s.bind(address)

s.bind(address) 将套接字绑定到地址

2.3、s.listen(backlog)

开始监听传入连接

2.4、sk.setblocking(bool)

是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。

2.5、sk.accept()

接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
2.6、sk.connect(address)

连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误

2.7、sk.close()

关闭套接字

2.8、sk.recv(bufsize[,flag])

接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

2.9、sk.send(string[,flag])

将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。

2.10、sk.sendall(string[,flag])

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

2.11、sk.settimeout(timeout)

设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

2.12、sk.getpeername()

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)

3、基本Socket实例

socket_server.py:

import socket
server = socket.socket()
server.bind(('localhost',9999)) #绑定要监听的接口
server.listen() #监听
print("我要开始等电话了")
while True:
  conn, addr = server.accept() # 接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来
  print("电话来了")
while True:
    data = conn.recv(1024) #等着收数据
    #为了防止收到是数据是Ctrl + C,是Ctrl + C的话会出现异常,其实就是收到的数据一直是空,判断如果是,就重新监听
    if not data:
      print("lost...")
      break
    conn.send(data.upper())
server.close()
socket_client.py:
import socket
client = socket.socket() #声明socket类型,同时生成socket链接对象
client.connect(('localhost',9999))
while True:
  msg = input(">>:").strip() #不能Send空
  if len(msg) == 0:
    continue
  client.send(msg.encode("utf-8"))
    """在 Python3 中,bytes 和 str 的互相转换方式是
str.encode('utf-8') 转成byte
bytes.decode('utf-8') 转成str"""

  data = client.recv(1024)
  print(data.decode('utf-8')) #decode默认就解成unicode
client.close()
    

4、通过socket实现简单得SSH
socket ssh server:
server.bind('localhost',9999)
server .listen()
while True:
  conn,addr = server.accept()
  print("new conn",addr)
  whilt True:
    print("等待客户指令")
    data = conn.recv(1024)
    if not data:
      print("客户端已断开")
      break
    print("执行指令:"data)
    cmd_res = os.popen(data.decode()).read #接收字符串的命令,数目相关执行结果
    conn.send(cmd_res.encode('utf-8')) #如果服务端没有任何执行结果,是不会客户端发送数据的
    print('send done') 
socket ssh client:
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 = client.recv(1024)
  print("命令结果大小:",cmd_res_size.decode())

client.close()

以上会出现,加入服务端对输入的命令无响应,客户端就会进入等待收命令的假死状态,而且由于接收缓存区的问题,会出现粘包的问题。
解决方法粘包的方法,就是先发数据的大小,客户端收到数据大小之后,进行比较,一直不断的接受,直到收到的完整的数据

5、socket优化版import socket
socket ssh server:
import socket,os
server = socket.socket()
server.bind(('localhost',9999))
server.listen()
while True:
conn,addr = server.accept()
print("new conn:",addr)
while True:
print("等待客户指令")
data = conn.recv(1024)
if not data:
print("客户端已断开")
break
print("执行指令:",data)
cmd_res = os.popen(data.decode()).read() #接受字符串,执行结果也是字符串
print("before send",len(cmd_res.encode()))
if len(cmd_res) == 0:
cmd_res = "cmd has no output..."
conn.send(str(len(cmd_res.encode())).encode('utf-8')) #先发大小给客户端
aaa = conn.recv(1024) #等待确认
print(aaa.decode())
conn.send(cmd_res.encode('utf-8')) #客户端没有收到数据,因为没有数据,服务器是不会发的
print("send done")

server.close()

socket ssh client:
client = socket.socket()
client.connect(('localhost',9999))

while True:
cmd = input(">>:").strip()
# print(cmd)
if len(cmd) == 0:
continue
client.send(cmd.encode('utf-8'))
cmd_res_size = client.recv(1024) #收到要传数据的大小
client.send(b"len is ok") #为了防止和下边即将收到的数据粘包,对收到的数据进行回应,此种方法只能针对小于size大小的数据包
print("命令结果大小:",cmd_res_size.decode())
received_size = 0
total_size = int(cmd_res_size.decode())
received_data = b''
while received_size < total_size: #为了防止粘包,对收到的数据进行处理
if total_size - received_size > 1024: #只要未收到的大于1024就赋值1024,要不然赋值剩余的收取的数据
size = 1024

else:
size = total_size - received_size
print("last receive:", size)
data = client.recv(size)
received_size += len(data)
received_data += data

else:
print("cmd res recevie done...",received_size)
print(received_data.decode())
#后边就可以继续加要收取的数据了,这样子和上边的数据就不会粘包了

client.close()


6、socket简单的ftp服务器
import socket,os,hashlib
server = socket.socket()
server.bind(('localhost',9999))
server.listen()
while True:
  conn,addr = server.accpect()
  print("new conn:",addr)
  while True:
    print("等待指令”)
    data = conn.recv(1024)
    if not data:
      print("客户端已经断开")
      break
    cmd,filename = data.decode().split() #默认已空格进行分割
    print(filename)
    if os.path.isfile(filename):
      f = open(filename,'rb')
      m = hashlib.md5()
      #stat 系统调用时用来返回相关文件的系统状态信息,后边很多属性,这次只用了st_size的属性
      file_size = os.stat(filename).st_size #文件的大小
      conn.send(str(file_size).encode()) #send file size
      conn.recv(1024) #wait for ack
      for line in f: #文件都是可以按照行读取的,这种不是一次性读取,这种方式是迭代器
        conn.send(line)
        m.update(line)
      f.close()
      conn.send(m.hexdigest().encode())
    print(‘send done’)
server.close()

    
ftp client:
 import socket,hashlib
client = socket.socket()
client.connect(('localhost',9999))

while True:
cmd = input(">>:").strip()
if len(cmd) == 0:continue
# python startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False
if cmd.startswith("get"):
client.send(cmd.encode())
server_response = client.recv(1024)
print("server respone:",server_response)
client.send(b"ready to recv file")
m = hashlib.md5()
file_total_size = int(server_response.decode())
received_size = 0
filename = cmd.split()[1]
print(filename)
f = open(filename + ".new","wb")
while received_size < file_total_size:
if file_total_size - received_size > 1024:
size = 1024
else:
size = file_total_size - received_size
print("last receive:",size)
data = client.recv(size)
received_size += len(data)
m.update(data)
f.write(data)
else:
print("file recv done",received_size,file_total_size)
new_file_md5 = m.hexdigest()
f.close()
server_file_md5 = client.recv(1024)
print("server file md5:",server_file_md5.decode())
print("client file md5:",new_file_md5)
client.close()


   
7、socket 用类来写
1、导入socketserver 模块
2. 继承socketserver.BaseRequestHandler类
3.重写handle方法,在handle方法中实现所有的socket内容     

4.实例化
socket server:

import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
  def handle(self):
    while True:
      try:
        self.data = self.request.recv(1024).strip() #request方法就是接收或者发送数据
        if not self.data:
          print("lost.....")
        else:
        # str.format() 其实是将括号中的文字放到前面
           print("{} write:".format(self.client_address[0])) #这些client_address都是继承父类的属性
                    print(self.client_address)
print(self.data)
self.request.send(self.data.upper())
except Exception as e:
print("err",e)
break
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
server = socketserver.ThreadingTCPServer((HOST, PORT),MyTCPHandler)
#执行 BaseServer.server_forever 方法,While 循环一直监听是否有客户端请求到达 ...
server.serve_forever()
socket client:
import socket,sys
HOST, PORT = 'localhost',9999
#sys.argv[1:] 就是提取传入的参数1包括后面的变量
# data = " ".join(sys.argv[1:])
# Create a socket (SOCK_STREAM means a TCP socket)
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #这是默认参数,可以直接写成 socket.socket()
sock.connect((HOST,PORT))
while True:
data = input(">>:").strip()
sock.sendall(data.encode('utf-8'))
received = str(sock.recv(1024).decode())
print("Sent: {}".format(data))
print("Received: {}".format(received))
sock.close()
 
原文地址:https://www.cnblogs.com/qianyuyu/p/10120046.html