博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
网络通信
阅读量:5142 次
发布时间:2019-06-13

本文共 11382 字,大约阅读时间需要 37 分钟。

基于TCP网络通信

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

简易版网络通信模拟xshell
服务端

# 服务端serverimport subprocessimport socketphone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  # 拿到一个socket对象# 调用sock.setsockopt设置这个socket选项,本例中把socket.SO_REUSEADDR设置为1,表示服务器端进程终止后,# 操作系统会为它绑定的端口保留一段时间,以防其他进程在它结束后抢占这个端口。phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)phone.bind(("127.0.0.1", 8080)) # 绑定唯一一个软件的地址# 调用sock.listen通知系统开始侦听来自客户端的连接,参数是在队列中的最大连接数。phone.listen(5)print("starting...")while True:           # 服务器循环    conn,addr=phone.accept() # 卡在这里直到,客户端返回一个元组,里面包含了socket对象以及客户端的地址    # print("客户端对象", conn)    print("客户端的ip地址", addr)    while True:       # 通信循环        try:            data = conn.recv(1024)            print("客户端发送的消息是", data)            # conn.send(data.upper())            res = subprocess.Popen(data.decode("gbk"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)            data1 = res.stderr.read()            data2 = res.stdout.read()            conn.send(data1)            conn.send(data2)        except Exception:            break    conn.close()    #  关闭客户端套接字对象phone.close()       # 关闭服务端套接字对象

客户端

# 客户端Clientimport socketphone = 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"))    data2 = phone.recv(1024)     # 指定从缓存中取数据的最大字节    print(data2.decode("gbk"))phone.close()     # 关闭客户端套接字对象

基于UDP网络通信

UDP服务端

# UDP通信之服务端import socketudpserver = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# udpserver.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)udpserver.bind(("127.0.0.1",8080))while True:   # 通信循环    data, client_addr = udpserver.recvfrom(1024)    print(data.decode("utf-8"))    # dada    print(client_addr)             # ('127.0.0.1', 60167)    udpserver.sendto(data.upper(), client_addr)

UDP客户端

# UDP通信之客户端import socketudpclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)server_ip_port = ("127.0.0.1", 8080)while True:    inp = input(">>:")    udpclient.sendto(inp.encode("utf-8"), server_ip_port)    data,server_addr = udpclient.recvfrom(1024)    print(data.decode("utf-8"))

粘包现象

只有TCP有粘包现象,UDP永远不会粘包

在上述简易通信模型里,在客户端执行ipconfig,在执行cls。发现并未一次取完ipconfig的执行结果,执行cls时,仍打印ipconfig的结果,那是因为ipconfig执行结果大于1024字节,而客户端收数据时,只收取1024个字节。切客户段服务端都是从操作系统的缓存中拿数据。
粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
用json和struct解决方案
struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes

# 把数字转换成四个字节的bytes,数字须小于int最大长度struct.pack('i',12345678)   # b'Na\xbc\x00'

解决粘包

服务端

# 服务端(解决粘包问题)import subprocessimport socketimport structimport jsonphone = 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)print("starting...")while True:    conn,addr=phone.accept() # c端连接成功返回一个元组,里面包含了socket对象以及客户端的地址    # print("电话线路是", conn)    print("客户端的手机号是", addr)    while True:        try:            data = conn.recv(1024)            print("客户端发送的消息是", data)            # conn.send(data.upper())            res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)            data1 = res.stderr.read()  # dos错误的运行结果            data2 = res.stdout.read()  # dos正确的运行结果            data_size = len(data1)+len(data2)   # 得到原始数据总大小            print(data_size)            data_dic = {"size": data_size}   # 为避免粘包,自定制报头            data_json = json.dumps(data_dic)   # 序列号报头            data_json_bytes = data_json.encode("utf-8")    # 序列化并转成bytes,用于传输            print(data_json)            data_len = struct.pack("i", len(data_json_bytes)) # 打包得到报头长度            # part1: 先发送报头的长度            conn.send(data_len)            # part2: 发送报头            conn.send(data_json_bytes)            # part3: 发送原始数据            conn.send(data1)            conn.send(data2)        except Exception:            break    conn.close()phone.close()

客户端

# 客户端(解决粘包问题)import socketimport structimport jsonphone = 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"))    head_dic_len = phone.recv(4)          # part1: 接受报头的长度    head_dic_size = struct.unpack("i", head_dic_len)[0]  # 取出报头长度    head_json = phone.recv(head_dic_size)   # part2: 接受报头    head_dic = json.loads(head_json.decode("utf-8"))  # 反序列化拿到报头数据    data_size = head_dic["size"]   # 拿出原始数据大小    recv_size = 0    recv_data = b""    while data_size > recv_size:        data = phone.recv(1024)   # part3: 接受原始数据        recv_size += len(data)        recv_data += data    print(recv_data.decode("gbk"))phone.close()

socketserver实现并发

1108839-20170513124731035-1045545225.png

TCP服务端

# 基于socketserver并发TCP通信# 服务端import  socketserverclass FtpServer(socketserver.BaseRequestHandler):    def handle(self):        # TCP下的request就是conn,客户端套接字        print(self.request)   #  return self.socket.accept()        while True:            data=self.request.recv(1024)            print(data.decode("utf-8"))            self.request.send(data.upper())if __name__ == '__main__':    obj = socketserver.ThreadingTCPServer(("127.0.0.1",8080),FtpServer)    print(obj.server_address)    # print(obj.RequestHandlerClass)    # print(obj.socket)    obj.serve_forever()  # 链接循环

TCP客户端

# 于socketserver并发TCP通信# 客户端import  socketserver_addr = ("127.0.0.1",8080)phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)phone.connect(server_addr)while True:  # 通讯循环    inp = input(">>:")    phone.send(inp.encode("utf-8"))    data = phone.recv(1024)    print(data)

并发UDP服务端

# UDP通信并发# 服务端import socketserverclass UdpServer(socketserver.BaseRequestHandler):    def handle(self):        # UDP协议下的request=(rec_data, self.socket)        # data, client_addr = self.socket.recvfrom(self.max_packet_size)        # return (data, self.socket), client_addr        print(self.request[0])   # (data, self.socket)        self.request[1].sendto(self.request[0].upper(), self.client_address)if __name__ == '__main__':    server_ip_port = ("127.0.0.1", 8080)    obj = socketserver.ThreadingUDPServer(server_ip_port, UdpServer)    print(obj.socket)    obj.serve_forever()

并发UDP客户端

# UDP通信并发# 客户端import socketudpclient = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)server_ip_port = ("127.0.0.1", 8080)while True:    inp = input(">>:")    udpclient.sendto(inp.encode("utf-8"), server_ip_port)    data,server_addr = udpclient.recvfrom(1024)    print(data.decode("utf-8"))

作业

简易FTP实现

服务端

# ftp上传和下载(面向对象)# 服务端import socketimport jsonimport osimport structclass FtpServer:    address_family = socket.AF_INET    socket_type = socket.SOCK_STREAM    allow_reuse_address = False    max_packet_size = 8192    coding = "utf-8"    request_queue_size = 5    server_dir = r"C:\\Users\\Zou\\PycharmProjects\\py_fullstack_s4\\day37\\粘包"    def __init__(self, server_address, bind_and_activate=True):        """构造函数"""        self.server_address = server_address        self.phone = socket.socket(self.address_family,self.socket_type)        if bind_and_activate:            try:                self.server_bind()                self.server_activate()            except:                self.server_close()                raise    def server_bind(self):        """绑定socket对象"""        if self.allow_reuse_address:            self.phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)        self.phone.bind(self.server_address) # 绑定地址和端口号        self.server_address = self.phone.getsockname()    def server_activate(self):        """服务端对象listen"""        self.phone.listen(self.request_queue_size)    def server_close(self):        """关闭服务端socket对象"""        self.phone.close()    def get_request(self):        """服务端开始监听accept"""        return self.phone.accept()    def close_request(self, request):        """关闭客户socket对象"""        request.close()    def run(self):        while True:   # 连接循环            self.conn, self.client_addr = self.get_request()            print("客户端地址:", self.client_addr)            while True:   # 通信循环                try:                    head_struct = self.conn.recv(4)                    print(head_struct)                    if not head_struct:break                    head_len = struct.unpack("i",head_struct)[0] # 拿到报头长度                    head_json = self.conn.recv(head_len).decode(self.coding) # 拿到json字符串                    head_dic = json.loads(head_json)                    print(head_dic)  # 打印用户字典                    # head_dic = {"cmd":"put", "file_name":"a.txt", "file_size":12345}                    cmd = head_dic["cmd"]                    if hasattr(self, cmd):                        func = getattr(self,cmd)                        func(head_dic)                except Exception:                    break    def put(self, dic):        """文件上传函数"""        file_path = os.path.normpath(os.path.join(            self.server_dir,            dic["file_name"]        ))        file_size = dic["file_size"]        recv_size = 0        print("-------",file_path)        with open(file_path,"wb") as f:            while recv_size < file_size:                recv_data = self.conn.recv(self.max_packet_size)                f.write(recv_data)                recv_size += len(recv_data)                print("recvsize:%s filesize:%s"% (recv_size, file_size))tcpserver1 = FtpServer(("127.0.0.1",8080))tcpserver1.run()

客户端

# FTP客户端(面向对象)import socketimport structimport jsonimport osclass FtpClient:    address_family = socket.AF_INET    socket_type = socket.SOCK_STREAM    allow_reuse_address = False    max_packet_size = 8192    coding = "utf-8"    request_queue_size = 5    def __init__(self, server_address, connect=True):        self.server_address = server_address        self.phone = socket.socket(self.address_family, self.socket_type)        if connect:            try:                self.client_connect()            except Exception:                self.client_close()    def client_connect(self):        self.phone.connect(self.server_address)    def client_close(self):        self.phone.close()    def run(self):        """客户端主逻辑"""        while True:            inp = input(">>:").strip()   # put a.txt            if not inp:continue            l = inp.split()            cmd = l[0]            if hasattr(self, cmd):                func = getattr(self, cmd)                func(l)    def put(self,l):        cmd = l[0]        filename = l[1]        if not os.path.isfile(filename):            print("%s文件不存在" % filename)            return        filesize = os.path.getsize(filename)        # head_dic = {"cmd":"put", "file_name":"a.txt", "file_size":12345}        head_dict = {"cmd":cmd, "file_name":filename, "file_size":filesize}        head_json = json.dumps(head_dict).encode(self.coding)        head_len = struct.pack("i", len(head_json))        self.phone.send(head_len)        self.phone.send(head_json)        send_size = 0        with open(filename, "rb") as f:            for line in f:                self.phone.send(line)                send_size += len(line)                print(send_size)            else:                print("文件上传成功")client = FtpClient(("127.0.0.1",8080))client.run()

转载于:https://www.cnblogs.com/zouruncheng/p/6814167.html

你可能感兴趣的文章
[转]ExtJs4 笔记(13) Ext.menu.Menu 菜单、Ext.draw.Component 绘图、Ext.resizer.Resizer 大小变更...
查看>>
1-5-06:奥运奖牌计数
查看>>
Windows下Python连接sqlite3数据库
查看>>
Javascript 类与静态类的实现(续)
查看>>
shim和polyfill有什么区别
查看>>
Failed to load the JNI shared library “E:/2000/Java/JDK6/bin/..jre/bin/client/jvm.dll
查看>>
Zabbix3.4服务器的搭建--CentOS7
查看>>
〖Python〗-- IO多路复用
查看>>
栈(括号匹配)
查看>>
夜太美---酒不醉--人自醉
查看>>
Java学习 · 初识 面向对象深入一
查看>>
zabbix经常报警elasticsearch节点TCP连接数过高问题
查看>>
源代码如何管理
查看>>
vue怎么将一个组件引入另一个组件?
查看>>
多线程学习笔记三之ReentrantLock与AQS实现分析
查看>>
【转】进程与线程的一个简单解释
查看>>
getopt,getoptlong学习
查看>>
数据的传递 变量与参数的使用
查看>>
Razor项目所感(上)
查看>>
移动互联网服务客户端开发技巧系列
查看>>