交互原理:

服务端和客户端通过底层socket接口编程通信,交互的信息都是通过byte字节形式传递,网络传输中不能保证信息完整传输有可能是分片传输,所以可能从缓冲区获取的信息需要分段拼接或拆分组合成一段段完整的信息读取;现在为了避免信息不完整,一般是通过给信息加上一个头部信息(一般存储了对信息长度和规范的描述)进行判断

题目:实现一个简单的客服聊天系统

客服中心是tcp服务端程序,客户使用tcp客户端程序。
连接成功后, 客户端发送给服务端第一个消息必须告诉客服中心用户的名字
一个客户连接后,别的客户不能连接, 等到前面的客户断开连接后,才能连上。
客户端和服务端都是手动在终端输入信息,发送消息后,必须等待接受到对方的消息才能发送下一个消息。
我们定义消息的格式如下:
0008|1|nickname,用竖线隔开3部分的字段,分别表示 消息长度、 消息类型 、消息内容 前面字段是4个字节的字符串,比如'0008',其内容是数字,表示消息的长度, 不足4个字节前面补零。
后面用竖线隔开的字段,是1个字节的字符串,是消息类型,其内容是数字,1表示客户昵称, 2表示普通消息 前面两个字段合起来 0008|1|,可以看成是一个消息的头部,nickname 是消息体
再后面用竖线隔开的字段是消息内容,其长度等于前面消息长度字段指明的长度减去消息头部长度(也就是7个字节)

解答:

服务端Server:

# coding=utf8
import sys
from socket import socket,AF_INET,SOCK_STREAM HOST = ''
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT) class CloseSocketError(Exception):
pass # 一个 ConnectionHandler 处理和一个客户端的连接
class ConnectionHandler:
# 0008|1|nickname
LEN_MSG_LEN_FIELD = 4
LEN_MSG_LEN_TYPE_FIELD = 7 def __init__(self,sock):
# 消息缓存区
self._readbuffer = b''
self.sock = sock
self.customername = '' # msgBody 是 unicode
@staticmethod
def encode(msgType,msgBody):
rawMsgBody = msgBody.encode('utf8') msgLenth = '{:04}' \
.format(len(rawMsgBody)+ConnectionHandler.LEN_MSG_LEN_TYPE_FIELD) \
.encode() msgType = f'{msgType}'.encode()
return b'|'.join([msgLenth,msgType,rawMsgBody]) @staticmethod
def decode(rawmsg):
msgType = int(rawmsg[5:6]) # 这样写rawmsg[5] 返回的是字节对应的数字
msgbody = rawmsg[ConnectionHandler.LEN_MSG_LEN_TYPE_FIELD:].decode('utf8')
return [msgType,msgbody] def readMsg(self):
bytes = self.sock.recv(BUFSIZ) # ** 用不同的返回值表示不同的含义 # 当对方关闭连接的时候,抛出异常
if not bytes:
self.sock.close()
raise CloseSocketError() # 应用程序的读取缓冲,和前面讲的系统的读取缓冲是两个不同的缓冲
self._readbuffer += bytes buffLen = len(self._readbuffer) # 如果已经获取了消息头部 (包括 消息长度,消息类型)
if buffLen >= self.LEN_MSG_LEN_TYPE_FIELD:
msgLen = int(self._readbuffer[:self.LEN_MSG_LEN_FIELD])
# 缓存区消息 已经包含了一个整体的消息(包括 消息长度,消息类型,消息体)
if buffLen >= msgLen:
# 从缓存区,截取整个消息
msg = self._readbuffer[0:msgLen]
# 缓存区变成剩余的消息部分
self._readbuffer = self._readbuffer[msgLen:] return self.decode(msg) # 如果已经获取的消息还不包括一个完整的消息头部, 不做处理等待下面继续接受消息
else:
return None print('get:%s' % bytes) # msgBody 是 unicode
def sendMsg(self,msgType,msgBody):
self.sock.sendall(self.encode(msgType,msgBody)) def handleMsg(self,msgType,msgBody):
# 客户名称
if msgType == 1:
self.customername = msgBody
print('客户名称设置:%s' % self.customername) # 普通消息
elif msgType == 2:
print(msgBody)
print('---------------')
# 客服输入消息内容
msgSend = input('>>')
self.sendMsg(2,msgSend) # 主循环,不断的接受消息发送消息
def mainloop(self):
while True:
try:
msg = self.readMsg()
# msg 里面包含了 type 和body
if msg:
msgType,msgBody= msg
self.handleMsg(msgType,msgBody)
except CloseSocketError:
print('对方断开了连接,等待下一个客户')
break
except IOError:
print('对方断开了连接,等待下一个客户')
break #创建socket,指明协议
tcpSerSock = socket(AF_INET, SOCK_STREAM) #绑定地址和端口
tcpSerSock.bind(ADDR) tcpSerSock.listen(5) print('等待客户端连接...')
while True:
#阻塞式等待连接请求
tcpCliSock, addr = tcpSerSock.accept()
print('有客户连接上来', addr) handler = ConnectionHandler(tcpCliSock)
handler.mainloop() tcpSerSock.close()

客户端Client:

# coding=utf-8
from socket import *
import traceback,sys HOST = 'localhost'
PORT = 21567
BUFSIZ = 1024
ADDR = (HOST, PORT) class CloseSocketError(Exception):
pass class ConnectionHandler:
# 0008|1|nickname
LEN_MSG_LEN_FIELD = 4
LEN_MSG_LEN_TYPE_FIELD = 7 def __init__(self,sock):
# 消息缓存区
self._readbuffer = b''
self.sock = sock
self.customername = '' # msgBody 是 unicode
@staticmethod
def encode(msgType,msgBody):
rawMsgBody = msgBody.encode('utf8') msgLenth = '{:04}' \
.format(len(rawMsgBody)+ConnectionHandler.LEN_MSG_LEN_TYPE_FIELD) \
.encode() msgType = f'{msgType}'.encode()
return b'|'.join([msgLenth,msgType,rawMsgBody]) @staticmethod
def decode(rawmsg):
msgType = int(rawmsg[5:6])
msgbody = rawmsg[ConnectionHandler.LEN_MSG_LEN_TYPE_FIELD:].decode('utf8')
return [msgType,msgbody] def readMsg(self):
bytes = self.sock.recv(BUFSIZ) # ** 用不同的返回值表示不同的含义 # 当对方关闭连接的时候,抛出异常
if not bytes:
self.sock.close()
raise CloseSocketError() self._readbuffer += bytes buffLen = len(self._readbuffer) # 如果已经获取了消息头部 (包括 消息长度,消息类型)
if buffLen >= self.LEN_MSG_LEN_TYPE_FIELD:
msgLen = int(self._readbuffer[:self.LEN_MSG_LEN_FIELD])
# 缓存区消息 已经包含了一个整体的消息(包括 消息长度,消息类型,消息体)
if buffLen >= msgLen:
# 从缓存区,截取整个消息
msg = self._readbuffer[0:msgLen]
# 缓存区变成剩余的消息部分
self._readbuffer = self._readbuffer[msgLen:] return self.decode(msg) # 如果已经获取的消息还不包括一个完整的消息头部, 不做处理等待下面继续接受消息
else:
return None print('--> %s' % bytes) # msgBody 是 unicode
def sendMsg(self,msgType,msgBody):
self.sock.sendall(self.encode(msgType,msgBody)) def handleMsg(self,msgType,msgBody):
# 客户名称
if msgType == 2:
print(msgBody)
print('---------------') def userinputAndSend(self):
msgSend = input('>>')
self.sendMsg(2, msgSend) # 主循环,不断的接受消息发送消息
def mainloop(self):
# 先发送客户名称
userName = '***'
if len(sys.argv) > 1:
userName = sys.argv[1].decode(sys.stdin.encoding)
self.sendMsg(1,userName)
while True:
try:
self.userinputAndSend()
# print('reading...')
msg = self.readMsg()
if msg:
msgType,msgBody= msg
self.handleMsg(msgType,msgBody)
except CloseSocketError:
print('对方断开了连接,程序退出')
return
except IOError:
print('对方断开了连接,程序退出')
return #创建socket,指明协议
tcpCliSock = socket(AF_INET, SOCK_STREAM) #连接远程地址和端口
tcpCliSock.connect(ADDR) handler = ConnectionHandler(tcpCliSock)
handler.mainloop()

socket编程(python)的更多相关文章

  1. socket编程python+c

    python版: server: def socket_loop_server_function(): HOST = '192.168.56.1' PORT = 21567 sk = socket.s ...

  2. Python Socket 编程——聊天室示例程序

    上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...

  3. python网络编程-socket编程

     一.服务端和客户端 BS架构 (腾讯通软件:server+client) CS架构 (web网站) C/S架构与socket的关系: 我们学习socket就是为了完成C/S架构的开发 二.OSI七层 ...

  4. python学习道路(day8note)(抽象类,类的方法,异常处理,socket编程)

    1.#面向对象 #抽象接口 === 抽象类 #就是架构师给你一个架子,你们去写,如果满足不了直接报错 #python2 print("python2---抽象类".center(2 ...

  5. python socket编程详细介绍

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

  6. 转:python socket编程详细介绍

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

  7. 转:Python 的 Socket 编程教程

    这是用来快速学习 Python Socket 套接字编程的指南和教程.Python 的 Socket 编程跟 C 语言很像. Python 官方关于 Socket 的函数请看 http://docs. ...

  8. Python之路,Day8 - Socket编程进阶

    Python之路,Day8 - Socket编程进阶   本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...

  9. Day8 - Python网络编程 Socket编程

    Python之路,Day8 - Socket编程进阶   本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台 ...

  10. Python 基础之socket编程(二)

    Python 基础之socket编程(二) 昨天只是对socket编程做了简单的介绍,只是把socket通信的框架搭建起来,要对其中的功能进行进一步的扩充,就来看看今天的料哈! 一.基于tcp的套接字 ...

随机推荐

  1. #6029. 「雅礼集训 2017 Day1」市场 [线段树]

    考虑到每次除法,然后加法,差距会变小,于是维护加法lazytag即可 #include <cstdio> #include <cmath> #define int long l ...

  2. java提取字符串数字,Java获取字符串中的数字

    ================================ ©Copyright 蕃薯耀 2020-01-17 https://www.cnblogs.com/fanshuyao/ 具体的方法如 ...

  3. linux命令——find

    简介:find是Linux系统中的常用命令(应用程序)之一.它是用来在指定目录层次结构(指定目录的子目录以及子目录的子目录)中查找符合指定条件的文件和目录 一:语法结构 find [directory ...

  4. 诱人的 react 视频教程-基础篇(14 个视频)

    诱人的 react 视频教程-基础篇(14 个视频) 诱人的 react 视频教程 - 基础篇 #1 介绍「07:25」 诱人的 react 视频教程 - 基础篇 #2 create-react-ap ...

  5. .net core 开发 Windows Forms 程序

    我是一名 ASP.NET 程序员,专注于 B/S 项目开发.累计文章阅读量超过一千万,我的博客主页地址:https://www.itsvse.com/blog_xzz.html 引言 .net cor ...

  6. 纪中23日c组T2 2159. 【2017.7.11普及】max 洛谷P1249 最大乘积

    纪中2159. max 洛谷P1249 最大乘积 说明:这两题基本完全相同,故放在一起写题解 纪中2159. max (File IO): input:max.in output:max.out 时间 ...

  7. c#中for与foreach的使用

    for循环示例: static void Main(string[] args) { string[] s = new string[] { "a,b,c,d,e,f,g" }; ...

  8. C# 将List数据 导出到csv 文件

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Ref ...

  9. 在vue中使用elementUI饿了么框架使用el-calendar日历组件,实现自定义显示备忘录标注

    饿了么官网给的自定义例子是点击哪个日期在日期后面加个勾 而我们想要的是显示备忘录,像这样↓,日历上直接显示 这时候我们要把template里的代码改一下 <el-calendar> < ...

  10. LAMP集成环境搭建

    本教程是在centos6.8 64位系统下进行操作 我们首先准备搭建LAMP环境 LAMP指的是Linux.Apache.MySQL 和 PHP 如果要安装PHP5.5 需要 首先rpm -ivh h ...