python day9 socket编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python day9 socket编程相关的知识,希望对你有一定的参考价值。


首先说一下TCP/IP 协议,OS七层

技术分享

 

应用模型:

socket 软件抽象层(不负责发送数据,真正发送数据的是socket后面的协议)

技术分享

socket

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket和file的区别:

  • file模块是针对某个指定文件进行【打开】【读写】【关闭】
  • socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

技术分享

举个打电话的例子如图:

技术分享

技术分享
import socket

ip_port = (127.0.0.1,9999)

#买手机
s= socket.socket() #封装好了TCP协议 ,生成套接字

#买手机卡
s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式

#开机
s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待)
while True:
    #等待电话
    conn,addr = s.accept() #返回两个对象(conn=通信链路,addr是客户端ip+port)accetp(接受客户端请求过程是一个阻塞过程
    while True: #(用来接收一个连接重复发消息)
        try: #(捕捉客户端异常关闭ctrl+c)
            #收消息
            recv_data = conn.recv(1024) #接受1024字节 收消息,阻塞
            print(str(recv_data,encoding=utf-8))
            if len(recv_data) == 0:break #客户端如果退出,服务端将受到空消息,退出
            #发消息
            send_data = recv_data.upper()
            conn.send(send_data)
        except Exception:
            break

    #挂电话
    conn.close()
socket_server
技术分享
import socket
ip_port = (127.0.0.1,9999)

#买手机
s= socket.socket() #封装好TCP协议的

#拨号
s .connect(ip_port)
while True:
    #发送消息
    send_data = input(">>:").strip()
    if send_data == exit:break
    if len(send_data) == 0:continue

    s.send(bytes(send_data,encoding=utf8))  #2.0可以发字符串,3.0只能发字节

    #收消息
    recv_data = s.recv(1024)
    print(str(recv_data,encoding=utf-8))
#挂断
s.close()
socket_client

解决粘包问题:

服务端:1、send 数据长度

    4、recv 收到确认信息,开始下一步

客户端:2、recv 获取数据长度

    3、send 发送确认信息

    5、recv 循环接收

技术分享
import socket
import subprocess

ip_port = (127.0.0.1,9999)
s= socket.socket() #封装好了TCP协议 ,生成套接字
s.bind(ip_port) #绑定ip+协议+端口:用来唯一标示一个进程,ip_port必须是元组形式
s.listen(5) #定义可以挂起链接数(最大128个连接,队列等待)
while True:
    conn,addr = s.accept() #返回两个对象(conn=通信链路)accetp过程是一个阻塞过程
    while True:
        try:
            recv_data = conn.recv(1024) #接受1024字节
            print(str(recv_data,encoding=utf-8))
            if len(recv_data) == 0:break
        
            p = subprocess.Popen(str(recv_data,encoding=utf-8),shell=True,stdout=subprocess.PIPE) #执行系统命令,Windows平台命令的标准输出是gbk编码,需要转换
            res = p.stdout.read() #获取标准输出
            if len(res) == 0:  #执行错误命令,标准输出为空
                send_data = cmd error
            else:
                send_data = str(res,encoding=gbk) #解码 win系统的是gbk编码 gpk------>str------->utf8
            send_data1 = bytes(send_data,encoding=utf-8)
            ready_tag = Ready|%s % len(send_data1)
            conn.send(bytes(ready_tag,encoding=utf-8)) #发送数据长度
            feedback = conn.recv(1024) #start 接受确认信息
            feedback = str(feedback,encoding=utf-8)
            if feedback.startswith("Start"):
                conn.send(bytes(send_data,encoding=utf-8)) #发送数据
            #conn.send(bytes(send_data,encoding=‘utf-8‘)) #编码
        except Exception:
            break

   
    conn.close()
sk_server
技术分享
import socket
ip_port = (127.0.0.1,9999)

s= socket.socket() #封装好TCP协议的
s .connect(ip_port)   #链接服务端,如果服务端已经有一个链接,那么挂起

while True:     #基于connect建立的链接循环发消息
  
    send_data = input(">>:").strip()
    if send_data == exit:break
    if len(send_data) == 0:continue

    s.send(bytes(send_data,encoding=utf8))  #2.0可以发字符串,3.0只能发字节

    #解决粘包问题
    ready_tag = s.recv(1024)       #收取带数据长度的字节Ready|len
    ready_tag = str(ready_tag,encoding=utf-8)
    if ready_tag.startswith(Ready):     #Ready|9998
        msg_size = int(ready_tag.split(|)[-1])  #获取待接收数据长度
    start_tag = Start
    s.send(bytes(start_tag,encoding=utf-8))

    #基于已经收到的待接收数据长度,循环接收数据
    recv_size = 0
    recv_msg = b‘‘
    while recv_size < msg_size:
        recv_data = s.recv(1024)
        recv_msg += recv_data
        recv_size += len(recv_data)
        print(MSG SIZE %s RECE SIZE %s  % (msg_size,recv_size))

    print(str(recv_msg,encoding=utf-8))

s.close()
sk_client

总结: 

  1、python3.5的socket只能收发字节(2.7可以收发字符串)

  2、只客户端退出,服务器端不退出

  3、accept() 和recv()是阻塞的,阻塞的前提是基于链接正常(socket 除了accpet会阻塞,recv也会阻塞,如果发的是空就会阻塞)

  4、listen(n)n代表:能刮起的链接数,如果n=1,代表可以链接一个,挂起一个,第三个拒绝。

  5、服务端出现端口冲突:修改监听端口号

 

socket 多并发

技术分享
import socketserver
import subprocess

class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        #conn = self.request
        self.request.sendall(bytes(欢迎致电10086,请输入0转人工服务,encoding=utf-8))
        while True:
            data = self.request.recv(1024)
            if len(data) == 0:break
            print("[%s]says:%s"%(self.client_address,data.decode()))
            #self.request.sendall(data.upper())
            cmd = subprocess.Popen(data.decode(),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            cmd_res = cmd.stdout.read()
            if not cmd_res:
                cmd_res = cmd.stderr.read()
            if len(cmd_res.strip()) == 0:
                cmd_res = bytes("cmd no msg",encoding=utf-8)
            self.request.sendall(cmd_res)
if __name__ == __main__:
    server = socketserver.ThreadingTCPServer((0.0.0.0,8009),MyServer)
    server.serve_forever() #永远接收
handle_server
技术分享
import socket
ip_port = (127.0.0.1,8009)

#买手机
s= socket.socket() #封装好TCP协议的

#拨号
s .connect(ip_port)

welcome_msg = s.recv(1024)
print("from server:",welcome_msg.decode())
while True:
    send_data = input(">>:").strip()
    if len(send_data) == 0:continue

    s.send(bytes(send_data,encoding=utf8))  #2.0可以发字符串,3.0只能发字节

    #收消息
    recv_data = s.recv(1024)
    print(str(recv_data,encoding=utf-8))
#挂断
s.close()
handle_client

 

以上是关于python day9 socket编程的主要内容,如果未能解决你的问题,请参考以下文章

python之day9(socket)

python基础-day9-函数

Python 之 Socket编程(TCP/UDP)

day9_面向对象编程

python学习道路(day9note)(socketserver编程,ftp)

day9