博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
21网络编程(socket、黏包现象、socketserver模块)
阅读量:5121 次
发布时间:2019-06-13

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

前言:鉴于socket实际工作基本不会直接接触,但是面试经常问。所以不花太多时间在这里。

1、利用socket来实现最基础的网络通信。

  服务端:

# coding:utf-8import socket# 创建socket对象server = socket.socket()# 绑定IP和端口server.bind(('127.0.0.1', 8000))# 连接数server.listen(5)# 等待客户端来连接,如果没人来就等待# conn是客户端和服务端连接的对象(伞),服务端以后要通过该对象进行收发数据。# addr是客户端的地址信息conn, addr = server.accept()# 通过对象去获取,(客户端通过伞发送的消息)data = conn.recv(1024)print(data)# 服务端通过连接对象(伞)给客户端回复消息conn.send(b'stop')# 与客户端断开连接(放开伞)conn.close()# 关闭服务端的连接。server.close()

  用户端:

import socketclient = socket.socket()# 向服务端发起连接请求(递伞)# 阻塞,知道连接成功后才会继续向下走client.connect(('127.0.0.1', 8000))# 链接上服务端后,向服务端发送消息。client.send(b'hello')# 等待服务端回消息data = client.recv(1024)print(data)client.close()

  上述代码实现了一个很简单的功能。服务端等待用户连接。用户端连接之后发送一个hello然后关闭连接。服务端收到之后回复一个stop。然后断开连接,关闭服务。

 

2、黏包

  黏包产生的原因:连续多次消息的快速发送到接收方的缓存池中。接收方取的时候无法正确判断每次接收的消息长度(也就是无法正确区分消息与消息之间的边界值)。

  黏包产生的结果:最简单的是消息会乱掉。上次没接收完的消息,会混在下次接收的头部。具体看场景。

  解决黏包的方法:struct模块的pack和unpack两个方法。首先用pack对于字符串进行压缩,这样长度就固定了。然后接收后unpack解压缩,循环取值拼接(笨方法就是接收方每次接收完给个回复,然后发送方再进行下一次发送。还有一种就是发送方每次发送等待个1s左右。)

  如下:一个简单的文件上传的例子

  项目结构如下:

  

  客户端代码:

# coding:utf-8import socketimport osimport jsonimport structimport hashlibsock = socket.socket()sock.connect(('127.0.0.1', 8800))while True:    cmd = input("请输入命令:")    if cmd == 'exit':        break    action, filename = cmd.strip().split(" ")    filesize = os.path.getsize(filename)    if action == 'put':        file_info = {            "action": action,  # put            "filename": filename,  # 文件名            "filesize": filesize,  # 文件大小        }        file_info_json = json.dumps(file_info).encode('utf8')        ret = struct.pack('i', len(file_info_json))        print(len(ret))        # 发送file_info_json的打包长度        sock.send(ret)        # 发送file_info_json字节串        sock.send(file_info_json)        md5 = hashlib.md5()        # 发送文件数据        with open(filename, 'rb') as f:            for line in f:                sock.send(line)                md5.update(line)        data = sock.recv(1024).decode('utf8')        if data == '200':            print(md5.hexdigest())            md5_val = md5.hexdigest()            sock.send(md5_val.encode('utf8'))            valid = sock.recv(1024).decode('utf8')            if valid == '100':                print('文件上传成功')            else:                print('文件上传失败!')        else:            print('服务器出错了!')    else:        print('请输入正常的命令')

  服务端代码:

# coding:utf-8import socketimport jsonimport structimport hashlibsock = socket.socket()sock.bind(('127.0.0.1', 8800))sock.listen(5)while True:    print("server is working.......")    conn, addr = sock.accept()    while True:        # 接收json的打包长度        file_info_lenght_pack = conn.recv(4)        file_info_lenght = struct.unpack('i', file_info_lenght_pack)[0]        # 接收json字符串        file_info_json = conn.recv(file_info_lenght).decode('utf8')        file_info = json.loads(file_info_json)        action = file_info.get('action')        filename = file_info.get('filename')        filesize = file_info.get('filesize')        md5 = hashlib.md5()        # 循环接收文件        with open("put/"+filename, "wb") as f:            recv_data_length = 0            while recv_data_length < filesize:                data = conn.recv(1024)                recv_data_length += len(data)                f.write(data)                md5.update(data)                print("文件总大小:%s,已成功接收%s" % (filesize, recv_data_length))        print('接收成功')        conn.send(b'200')        print(md5.hexdigest())        md5_val = md5.hexdigest()   # 服务端的md5        client_md5 = conn.recv(1024).decode('utf8')     # 接收用户端的md5。会黏包。要处理下。        if md5_val == client_md5:            conn.send(b'100')        else:            conn.send(b'10000')

  注:这里因为最后用md5做了个文件一致性的对比。所以struct显得反倒没那么突出了。理解能用就行。

 

3、socketserver模块

import socketserver'''在socketserver模块中conn = self.request'''class Myserver(socketserver.BaseRequestHandler):    def handle(self):        '''        这里写逻辑代码        :return:        '''        pass# 相当于 1、创建socket对象   2、self.socket.bing()  3、self.socket.listen(5)server = socketserver.ThreadingTCPServer(('127.0.0.1', 8899), Myserver)# 相当于执行server.accept(),返回两个参数。server.serve_forever()

  socketserver就相当于把最开始的几行代码封装起来了。并且还开了个多线程。不过这里稍微了解下感觉就ok了。  

 

转载于:https://www.cnblogs.com/cbslock/p/11280567.html

你可能感兴趣的文章
大话文本检测经典模型:EAST
查看>>
待整理
查看>>
一次动态sql查询订单数据的设计
查看>>
C# 类(10) 抽象类.
查看>>
Vue_(组件通讯)子组件向父组件传值
查看>>
jvm参数
查看>>
我对前端MVC的理解
查看>>
Silverlight实用窍门系列:19.Silverlight调用webservice上传多个文件【附带源码实例】...
查看>>
2016.3.31考试心得
查看>>
mmap和MappedByteBuffer
查看>>
STM32单片机使用注意事项
查看>>
swing入门教程
查看>>
好莱坞十大导演排名及其代表作,你看过多少?
查看>>
Loj #139
查看>>
hihocoder1187 Divisors
查看>>
Azure 托管镜像和非托管镜像对比
查看>>
js window.open 参数设置
查看>>
032. asp.netWeb用户控件之一初识用户控件并为其自定义属性
查看>>
Ubuntu下安装MySQL及简单操作
查看>>
前端监控
查看>>