局域网通信工具LJ

少于 1 分钟读完

1. LJ简介

LJ类似于飞秋,使用Python + PyQt开发,有一个主界面和对话框,在主界面可以显示局域网内的在线主机,切换网卡等,在对话框可以进行一对一聊天和文件传输。主要包括功能包括:

  • 自动扫描在线主机,动态显示在好友列表;
  • 对于多网卡主机,可以自由切换网卡;
  • 文字聊天;
  • 文件传输;
  • 支持多会话;

2.系统设计

2.1.主构架

1)程序界面使用qt设计师设计,保存为ui文件,使用pyuic4命令生成对应的python脚本。总共使用的两个界面,一个主界面,包含在线主机列表、网卡信息等主要信息;另一个是聊天对话框,可以进行简单的文字聊天和文件传输。

2)获取本机网卡列表。使用windows的‘ipconfig /all’命令获取网卡详细信息,解析出‘本地连接’、‘无线局域网’和‘VMnet’(方便测试)对应的适配器信息。本工具会按‘本地连接’、‘无线局 域网’的优先级确定一个默认的网卡。在确定使用本地哪个网卡之后,获取其IP和mask,计算出广播地址broadcast_addr.

3)建立一个消息接收线程,它在控制端口6999循环接收消息,根据不同的命令做出不同的动作,详细介绍参见下面第2节。

4) 建立一个广播消息发送线程,它每15s向端口6999发送一次广播数据包。

5)“在线主机列表”显示局域网中运行着该程序的机器IP和主机名,双击“在线主机列表”中某一项,可打开对话,进行文字聊天和文件传输。

2.2.接收控制命令

  • 控制命令设计了3个:‘Alive’、‘Down’和‘SESSION’.
  • ‘Alive’:不断向局域网发送的广播包,表明自己在线;
  • ‘Down’:本地下线时,向局域网发送的广播包,表明自己下线;
  • ‘SESSION’:建立会话时

2.3.聊天和文件传输对话框

如果Host-A向Host-B发送消息或文件,如果还没有建立会话,那么先进行如下过程建立一个会话,建立会话的目的是使双方知道对方接收信息的端口recv_port,建立会话的过程如下图所示。

会话建立过程模仿TCP会话3次握手建立连接过程。 Host-A首先创建一个新的a-socket,此socket以后用于Host-A接收数据报 的端口,它向Host-B的控制端口发送‘SESSION’命令,表示想建立会话,如果Host-B接受建立会话请求,Host-B就打开对话框,创建一 个b-socket,用b-socket向a-socket发送‘ACCEPT SESSION’应答,表示接受建立会话请求,a-socket收到肯定应答后,向b-socket发送一个确认‘ESTABLISHED’。至此,双方 都知道了对方用于接收消息的端口,会话建立成功。

发送文字消息比较简单,下面说一下文件传输的交互过程。发送方A新建一个线程来发送文件,在线程中建立一个临时的套接字sfscok,它首先向接收 方B发送“FILE\r\n”命令,表示即将发送一个文件,紧接着发送文件名FileName,如果接收方B接收此文件,B会通过选中对话框右侧的文件列 表中的文件,然后点击接收文件,这时,接收方B会新建一个线程用来接收文件,在线程中建立一个套接字rfsock,rfsock向sfsock发送 ‘ACC’应答表示同意接收文件。然后发送方A打开文件FileName,为防止文件过大,A每次读取并发送1KB大小的文件内容;考虑到接收速率可能小 于发送速率,以及接收缓冲区的问题,B每接收到一个报文,就发送一个‘RECV_OK’的确认。发送完成A会向B发送一个‘FILE END\r\n’字符串表明文件发送完成。

#3.问题&解决方法

  1. 发送中文失败 UnicodeEncodeError: ‘ascii’ codec can’t encode characters in position 解决办法: 这是python 字符集的问题,在文件前加两句话: reload(sys) sys.setdefaultencoding( “utf-8″ )
  2. 发送大文件失败 没有考虑发送和接收速率不匹配的问题,导致发送大文件时,接收的比较少。 解决办法: 每次发送不超过1KB大小的内容,接收端收到后发送一个“RECV_OK”的确认,发送端收到确认后继续发送
  3. 窗口重新打开问题 A、B双方正在进行会话,A关闭窗口,再重新打开,这时怎么建立这个会话,因为A关闭对话框时已经销毁了会话信息。 解决办法: 每个程序维护一个正在打开的窗口列表,并保存相关的会话信息,如(remote_ip,remote_port).
  4. 对话框重复打开问题 对话窗口打开的四种情况 1)没有打开,对方请求对话 2)没有打开,本地用户双击开启对话 3)已经打开,本地用户又双击打开 4)已经打开,对方请求对话 针对情形3),用一个列表保存正在运行的对话框,忽略重复请求 针对情形4),更新一下remote_port, 并把local_port发送给对方
  5. 第一次发送信息等待问题 会话发起方第一次发送消息时,在未得到对方确认前,处理阻塞状态,由于使用了socket.recvfrom函数,一直阻塞等待响应,而不把发送信息放到历史信息框中。 解决办法: 使用线程来建立会话,但此时会出现第一发送的消息对方收不到的情况,原因是建立新的线程后,原程序继续向下执行,等会话建立后,父线程可能已经结束了,所以要在t.start()后加上t.join()来阻塞父线程。
  6. 新消息提醒 本实验使用弹出对话框的方式来提醒用户有人发来消息或文件。曾考虑过加入系统托盘图标来美化程序,用托盘图标弹出气泡信息来通知用户有人发来新消息(类似于QQ的消息通知),但是win7下气泡信息就是不出来,而在xp下可以正常弹出。

留下评论