基于Python的聊天软件的设计与实现

2017-05-18 03:40张萍萍纪志坚
网络安全技术与应用 2017年5期
关键词:服务器端聊天客户端

◆张萍萍 纪志坚

(青岛大学自动化与电气工程学院 山东 266071)

基于Python的聊天软件的设计与实现

◆张萍萍 纪志坚

(青岛大学自动化与电气工程学院 山东 266071)

本系统是一个局域网聊天软件,是以Python语言为基础,结合数据库技术,多线程编程技术,运用TCP模式的Socket编程技巧实现一个服务器与多个客户端互联,服务器存储并转发客户端发来的数据,从而实现一对一的客户端之间的通信。服务器将客户端发来的消息进行广播,客户端接收服务器发送的消息,通过内置标志位判断是否进行接收,从而实现聊天室群聊功能。本论文主要阐述了软件开发过程中运用软件开发瀑布模型所走过的需求分析,概要设计,详细设计,编码以及单元测试,集成测试等软件开发与测试流程。通过严谨的编码规范,全面的版本控制以及细致的缺陷跟踪力求设计出的软件功能完善,界面美观,性能优越。

Python语言;MySQL数据库;多线程编程;TCP协议;Socket编程

0 引言

当今主流的聊天工具有很多,例如 QQ、MSN、雅虎通,这些聊天软件已能满足用户聊天的需求。但由于这些软件的不开源性,对于很多公司企业来说,为了保证其信息的安全性,都不得不开发设计出一款自己的聊天软件。因此,从当前情况来说,一款安全、实时的聊天软件不仅能够保证员工之间的交流,同时也能保证企业内部之间信息交流的安全,这对于一个企业来说是极其重要的。

基于这种情况,本文设计开发出了该聊天软件,其最大的特点就是能够动态、实时的完成信息的传递以及能更有效的处理用户的请求。此外,也因其功能单一而更易于维护和更新。

1 系统分析

由于本系统是由更先进的面向对象编程语言Python编写,程序中大量引用的类库都具有平台无关性,这使得最终系统具有良好的跨平台特性[1]。可以将服务器端部署在Windows系统下而将客户端安装在Linux系统中,反之亦然。

1.1 可行性分析

(1)经济可行性:该聊天工具是一个小型的系统,因此就开发成本来说,只要有一台电脑,就可以开发出来,因此实际的成本我们也可以忽略不计。

(2)技术可行性:该聊天系统采用Python 作为编程语言,利用MySql 作为后台数据库。首先,对于网络编程Python提供了丰富的类库,使得我们能够很好的进行网络通信。其次,MySQL数据库本身具有体积小、速度快的特点。而该聊天系统的存储数据量不会很大,因此将MySQL 数据库作为后台数据库是一个不错的选择[2]。综上,该系统在技术上来说也是可行的。

(3)运行可行性:该系统是个比较小的系统,因此对于软件和硬件的要求都不是很高,现在的电脑基本上都能满足其要求。运行所需软件要求:

操作系统:Windows Win7/Linux 及以上

Python:Python 2.7及以上

运行所需硬件要求:

CPU :不作要求

内存:256M 及以上

1.2 需求分析

(1)服务器端功能需求:能够处理用户发送的各种请求(聊天信息、传送文件、添加好友等)并准确的转发到指定用户;能够向数据库注册用户信息;能够向数据库写入离线消息;

(2)客户端功能需求:用户可以登录;用户能够注册聊天账号;用户能够添加或者删除好友;用户能够发送文本聊天信息;用户能够进行文件传输;用户能够进行聊天室群聊;

(3)系统用例图:通过对系统的需求分析,可以识别出系统有两个参与者,一个是用户,另一个是系统管理员(在实际的代码实现中,配置好服务器端的 IP地址及端口号后服务器自动响应客户端请求,自动建立连接,关闭连接无需人为干预)。基于对系统的分析分别绘制出如图1,图2所示的用户请求和系统处理用户请求用例图[3]。

图1 用户请求服务用例图

图2 系统处理用户请求用例

系统处理用户请求用例图说明:

(1)Regeist:处理用户注册请求。

(2)Login:处理用户登录请求。

(3)addFriend:处理用户添加好友请求。

(4)removeFriend:处理用户删除好友请求。

(5)textChat:处理用户文本聊天请求。

(6)fileTrans:处理用户文件传输请求。

(7)roomChat:处理用户聊天室聊天请求。

(8)check user:用户注册时,验证用户名是否已经存在。

(9)Confirm:用户登录时,验证用户名密码是否匹配。

(10)isOnLine:标识用户的在线状态。

2 系统总体设计方案

2.1 概要设计

2.1.1 整体框架设计

本聊天系统主要采用了C/S 结构,服务端和客户端之间通过Socket 进行连接通信。服务端主要任务是:连接数据库和处理客户端的各种请求;客户端主要是为用户提供各种服务,然后将服务请求发送给服务端[4]。

2.1.2 模块设计

根据前期的系统需求分析,设计出局域网聊天系统的各个功能模块[5],如下图所示。

图3 客户端与服务器端连接框架图

图4 客户端之间连接框架

图5 各功能模块图

2.1.3 数据库设计

通过前期对系统进行需求分析以及对各个功能模块的设计,确定数据库存储用两张表实现,分别是用户信息表(userInfo)、历史信息表(history)表结构设计[6]如图6、图7所示。

图6 用户信息表

图7 聊天记录表

2.2 详细设计

2.2.1 客户端的设计

客户端主要是实现用户所需功能,当用户启动一个客户端时,用户可以通过图形画界面上的一些功能按钮开启某项对应的功能。当用户点击某个按钮时,会产生一个相应的事件,客户端监听到该事件后,启动相应的程序去处理该事件。并将该事件的请求发送到服务器,然后客户端等待服务器的响应[7]。这两项功能分别由init.py和Server.py两个模块实现,init模块包含建立客户端连接套接字,接收用户发送的请求,将请求发送给服务器端。Server模块是一个独立的模块要先于客户端程序运行,实现的功能为建立服务器端连接套接字,绑定 IP和端口地址,监听客户端请求,并做出响应[8]。客户端的流程图如图8所示。

图8 客户端程序流程图

2.2.2 登录设计

本局域网聊天软件名称为 PyChat本文所有的截图是本软件的1.0版本,py为Python的简写,同时Python脚本文件后缀名为.py,Chat意为“聊天,闲谈”,在开发本软件时用到的集成开发环境为PyCharm,PyChat的名字也与之相关联。

用户登录界面用于接收用户输入的用户名和密码,登录界面对用户输入的用户名密码进行验证(同数据库中用户信息表进行比对),验证成功则弹出联系人列表界面正常登录,验证失败则提示用户注册。新用户进行注册时,注册界面复用登录界面,由“注册”按钮调用数据库模块的相关代码,用户输入的用户名,密码信息插入数据库用户信息表中,插入成功后弹出提示窗口提示用户牢记用户名,唯一ID,密码。登录界面如图9所示,注册成功提示界面如图10所示。

图9 登录窗口

图10 注册成功提示界面

本软件代码部分共分为三个.py文件,分别为 init.py,Server.py,,oprate_db.py,登录界面由init.py文件中的Window类实现,此类初始化聊天窗口,创建两个Label控件分别提示帐号、密码,两个Text控件用于接收用户输入的用户名密码,两个Button控件用来产生鼠标点击事件,触发数据库操作函数。当用户需要注册新用户时只需将自己想要的用户名,密码输入到输入框中点击注册按钮即可[9]。

注册按钮调用 Mysql.register()方法将用户信息入库。点击登录按钮调用Client.Contact_window()类,初始化联系人列表界面。

2.2.3 联系人列表设计

联系人列表用于展示自己的联系人,联系人列表包括添加好友按钮,聊天室按钮,好友。界面效果如图11所示。

图11 联系人列表

图12 添加好友

联系人列表主要分为三部分,添加好友、聊天室、联系人,用户点击添加好友时调用 init().Contact_window()弹出好友添加界面如图12所示,好友添加成功后,以ID的形式显示在联系人中,点击联系人按钮调用init.Chat()弹出聊天界面。当用户点击聊天室按钮时,弹出聊天界面,登录系统的所有用户可以在此窗口中进行聊天室群聊。

2.2.4 聊天窗口设计

聊天窗口用于接收用户输入的聊天信息,同时将接收到的聊天信息显示到消息屏幕中,聊天窗口如图13 所示。

图13 聊天窗口

用户将需要发送的信息输入到窗口下方的文本输入框中,Client.send()方法将用户输入数据进行提取,将提取后的信息封装到Client建立的Socket中,用于同服务器进行通信。接收消息时,接收到的消息将显示到输入框上方的接收信息框中。

3 编码

本章展示底层通信原理代码,本聊天软件分为通信部分(也是核心部分)和界面部分,最终的软件实现是通信部分与界面部分的集成。本章展示底层通信原理代码,本聊天软件分为通信部分(也是核心部分)和界面部分,最终的软件实现是通信部分与界面部分的集成。

3.1 服务器部分

#!/usr/bin/env python

# coding: utf-8

import socket

import threading

"""定义用到的变量,建立服务器端套接字"""

con = threading.Condition()

HOST = '127.0.0.1'

PORT = 8888 # Arbitrary non-privileged port

data = ''

ADDR = (HOST,PORT)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print 'Server init success!'

s.bind(ADDR)

s.listen(10)

print 'Waiting for client connect'

#Function for handling connections. This will be used to create threads

# 接受数据存到tmp中 tmp传给NotifyAll方法中变成data

"""此方法用于接收客户端发送的消息,有用户离线时输出提示消息"""

def ClientThreadIn(conn, nick):

global data

global id

#infinite loop so that function do not terminate and thread do not end.

while True:

#Receiving from client

try:

temp = conn.recv(1024)

if not temp:

conn.close()

return

NotifyAll(temp)

print data

except:

NotifyAll(nick + " leaves the room!")

print data

return

#came out of loop

"""此方法用于唤醒其他线程,是实现多线程的一部分"""

def NotifyAll(sss):

global data

if con.acquire():

data = sss

con.notifyAll()

con.release()

"""此方法将消息从服务器端发送给客户端"""

def ClientThreadOut(conn, nick):

global data

while True:

if con.acquire():

con.wait()

if data:

try:

conn.send(data)

con.release()

except:

con.release()

return

"""主循环保证服务器端开启后一直处于运行状态

并不间断接收客户端发来的消息,根据实际需要将消息转发给特定用户

如果用户进行的是聊天室群聊,则将消息广播给所有用户,调用多线程

方法接收消息与发送消息同步进行"""

while True:

#wait to accept a connection - blocking call

conn, addr = s.accept()

print 'Connected with ' + addr[0] + ':' + str(addr[1])

nick = conn.recv(1024)

NotifyAll('Welcome ' + nick + ' to the room!')

print data

print str((threading.activeCount() + 1) / 2) + ' person(s)!'

# conn.send(data)

threading.Thread(target = ClientThreadIn , args = (conn,nick)).start()

threading.Thread(target = ClientThreadOut , args = (conn,nick)).start()

s.close()

3.2 客户端部分

#!/usr/bin/env python

# coding: utf-8

# author: Paula

# date: Mar-o3-2017

"""导入用到的包"""

from Tkinter import *

from oprate_db import * from time import sleep

import socket

import threading

"""将客户端的实现部分封装在此类中"""

class Client(object):

global msg_trans

msg_trans=''

def __init__(self,myId,name):

"""初始化socket"""

self.sock = socket.socket(socket.AF_INET ,socket.SOCK_STREAM)

self.sock.connect(('127.0.0.1',8888))

self.inString = ''

self.outString = ''

self.nick =name

self.sock.send(self.nick)

self.myId =myId

self.fromid = ''

def DealOut(self,s,toid,myid,outstring):

"""输出函数,将用户输入的信息通过建立的 socket连接发送到服务器端"""

toId = toid

myId = myid

msg = []

self.outString = outstring

self.outString = self.nick + ': ' + self.outString

msg.append(toId)

msg.append(myId)

msg.append(self.outString)

mssage=str(msg)

s.send(mssage)

def DealIn(self,s):

"""数据接入函数,将服务器发来的数据进行分拣,接收发送给自己的消息

或聊天室消息"""

global msg_trans

while True:

self.inString = s.recv(1024)

# print '2---7:', self.inString[2:7]

if self.inString:

If self.inString[2:7]==self.myId or self.inString[2:7]=='10000':

self.fromid = self.inString[11:16]

msg_trans = self.inString[21:-4]

print 'deal in',msg_trans

def mainrecv(self):

"""线程接收函数,封装多线程方法和数据接入函数"""

thin = threading.Thread(target = self.DealIn, args = (self.sock,)) thin.start()

# return

def mainsend(self,toid,outstr):

"""线程发送函数,封装多线程方法和数据发送函数"""

if self.inString[2:7]=='10000':

to = '10000'

elif self.fromid=='':

to = toid

else:

to = self.fromid

outstring = outstr

# self.sock.send(self.nick)

thout = threading.Thread(target = self.DealOut, args = (self.sock,to,self.myId,outstring,))

thout.start()

4 测试

测试部分主要分为,单元测试、集成测试,分别对应了开发过程中的编码、概要设计、需求分析部分,经过系统的测试可以暴露软件中存在的缺陷,提高代码可靠性,最终符合用户需求。

4.1 单元测试

单元测试是(模块测试)是开发者编写的一小段代码,用于检测被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为,是对单个的软件单元或者一组相关的软件单元进行的测试,是代码级别的测试。

经过测试客户端一对一单聊功能正常,客户端正常连接服务器,客户端可以接收其他用户发来的消息,并将自己的消息发还给其他用户。

4.2 集成测试

图14 客户端一对一聊天单元测试

集成测试又叫组装测试或联合测试,是单元测试的多级扩展,是在单元测试的基础上进行的一种有序测试,这种测试需要将所有模块按照设计需求,逐步装配成高层的功能模块,并进行测试,直到整个软件成为一个整体,集成测试旨在检验软件单元之间的接口关系,以期望通过测试发现各软件单元接口之间存在的问题,最终把经过测试的单元组成符合设计要求的软件。集成测试验证程序和概要设计说明的一致性,任何不符合该说明的程序模块行为都应该加以记载并上报,因此集成测试是发现和改正模块接口错误的重要阶段。

集成测试暴露出的问题:

(1)底层通信实现时未用类进行封装,导致代码耦合度高降低了模块独立性。

(2)底层通信实现时未预留足够的接口,导致界面无法与功能进行对接。

(3)接口间无法传递数值。

经过严谨的集成测试主要暴露了以上三个问题,通过不断对代码进行优化,对功能的内部实现进行封装,使用类变量和全局变量解决参数传递问题,定义了多个接口用于界面调用相应的功能代码,很好的解决的如上问题,降低了模块的耦合程度,提高了模块独立性。

5 结束语

本次开发的程序是PyChat的1.0版本,本文设计聊天软件以其信息传递迅速、占用系统资源少、易于维护、功能简单实用、信息安全、占用较少的网络资源等优势在企业、学校级别的大型局域网络中拥有广阔的运用前景。部署此局域网聊天系统仅需一台服务器就可实现具有跨平台特性的实时信息通讯。不过软件还有很多不完善的地方,还有很多缺陷等待我去改进。在测试部分发现,字体大小和字体颜色等功能还未实现;系统的健壮性有待提高,非正常流程结束程序会导致端口占用无法开启新的客户端等缺陷。因此我需要进一步提高软件的可用性,丰富软件的功能。

[1]Barry.P .林琪等译.Head First Python[M].北京:中国电力出版社,2012.

[2]Mark Summerifield.王弘博,孙传义译.Python3程序开发指南(第二版)[M].人民邮电出版社,2011.

[3]Magnus Lie Hetland.司维,曾军崴,谭颖华译.Python基础教程[M].北京:人民邮电出版社,2010.

[4]蔡建平.软件综合开发案例教程--Linux、GCC、MySQL、Socket、Gtk+与开源案例[M](1版).北京:清华大学出版社,2011.

[5] Ben Forta.钟鸣,刘晓霞译.SQL必知必会[M].北京:人民邮电出版社,2013.

[6]John Goerzen.莫迟译.Python网络编程基础[M].北京:电子工业出版社,2007.

[7]Wesley J.Chun.宋吉广译.Python核心编程[M].北京:人民邮电出版社,2008.

[8]沈洁元.简明 Python教程[M].北京:清华大学出版社,2009.

[9]Lasse Koskela.李贝译.测试驱动开发的艺术[M].人民邮电出版社,2010.

猜你喜欢
服务器端聊天客户端
Linux环境下基于Socket的数据传输软件设计
如何看待传统媒体新闻客户端的“断舍离”?
县级台在突发事件报道中如何应用手机客户端
孵化垂直频道:新闻客户端新策略
大枢纽 云平台 客户端——中央人民广播电台的探索之路
我就是不想跟你聊天了
敞开门聊天
基于Qt的安全即时通讯软件服务器端设计
基于Qt的网络聊天软件服务器端设计
基于C/S架构的嵌入式监控组态外设扩展机制研究与应用