`
cloverprince
  • 浏览: 130194 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

飞鸽02:朴素的实现

阅读更多
最朴素的实现就是过程化的方法。

简单起见,实现使用命令行界面。分成两个线程,一个用于接受键盘输入,另一个用于接受UDP Socket的输入。

警告:代码十分纠结,堪比意大利面条。初学者和OO程序员切勿模仿。

第一部分是初始化。

#!/usr/bin/env python

import threading
import socket
import constants    # 存放飞鸽协议需要的常数,一般为IPMSG_*的形式
import pdu     # 自动构造数据报报文的模块,略。

PORT = 2425    # 飞鸽使用2425端口
MAX_PACKET = 16384  # 任意设置的数值。报文的最大大小

BROADCAST_ADDR = ("255.255.255.255",2425)  # 广播地址。

SELF_USER = "Shirouzu"  # 本地用户名。应该通过操作系统接口获取。
SELF_HOST = "Jupiter"   # 本地主机名。

# 创建socket
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
s.bind(("",PORT))

# 因为Python语言的函数内部不能随意修改全局变量,
# 所以干脆创造一个简单的对象,其成员专门存放全局变量。
class SharedData(object):
    pass

shared_data = SharedData()
shared_data.cur_pkt_no = 1 # 当前报文编号。每次发出一个,加一。(注意:线程不安全)

shared_data.contacts = {}  # 储存邻居列表。关键字是IP地址;值是用户名、主机名、昵称等。
shared_data.nick = "ThisIsMyNickname" # 昵称
shared_data.readlist = []  # 待读的(加信封的)信息的队列。


下一部分是接收线程。从网络接收报文,逐一解析(在pdu模块中解析,这里省略),然后根据命令的类型进行下一步动作。

def receiver_proc():
    while True:
        # 接收,解析
        data,addr = s.recvfrom(MAX_PACKET)
        try:
            result = pdu.parse_pdu(data)
        except pdu.MalformedPduError:
            print "Malformed PDU:"
            print '[%s]'%data
            continue

        # 分离命令和标志
        cmd = pdu.get_mode(result["command"])
        opt = pdu.get_opt(result["command"])
        
        if cmd==constants.IPMSG_BR_ENTRY:
            # 用户新加入
            update_contact(addr,result['user'],result['host'],
                    pdu.is_set(opt,constants.IPMSG_ABSENCEOPT),
                    result['additional'])
            print "New client. user:[%s], host:[%s], addr:[%s]"%(
                    result["user"],result["host"],addr)
            reply=pdu.make_pdu(
                    1,
                    shared_data.cur_pkt_no,
                    SELF_USER,
                    SELF_HOST,
                    constants.IPMSG_ANSENTRY,
                    shared_data.nick,
                    )
            shared_data.cur_pkt_no+=1
            s.sendto(reply,addr)
        if cmd==constants.IPMSG_ANSENTRY:
            # 其他节点回应了自己的加入
            update_contact(addr,result['user'],result['host'],
                    pdu.is_set(opt,constants.IPMSG_ABSENCEOPT),
                    result['additional'])
            print "Answer entry: user:[%s], host:[%s], addr:[%s]"%(
                    result["user"],result["host"],addr)
        if cmd==constants.IPMSG_BR_ABSENCE:
            # 其他节点发布了状态变更消息
            update_contact(addr,result['user'],result['host'],
                    pdu.is_set(opt,constants.IPMSG_ABSENCEOPT),
                    result['additional'])
            print "User [%s] changed state: nick:[%s], away:%s" % (
                    result["user"],result["additional"],
                    pdu.is_set(opt,constants.IPMSG_ABSENCEOPT))
        if cmd==constants.IPMSG_SENDMSG:
            # 收到其他节点发来的消息
            print "Message from [%s]:"%result['user']
            print "   PKT_NO: [%d]:"%result['packet_num']
            print result['additional']
            if pdu.is_set(opt,constants.IPMSG_SENDCHECKOPT):
                reply = pdu.make_pdu(
                        1,
                        shared_data.cur_pkt_no,
                        SELF_USER,
                        SELF_HOST,
                        constants.IPMSG_RECVMSG,
                        result['packet_num'],
                        )
                shared_data.cur_pkt_no+=1
                s.sendto(reply,addr)
            if pdu.is_set(opt,constants.IPMSG_SECRETOPT):
                # 对于包含信封的信息,在将来予以回应。
                # 严格的说,也不应该立即显示信息内容。
                shared_data.readlist.append((
                    result['additional'],result['user'],
                    result['packet_num'],addr))
        if cmd==constants.IPMSG_RECVMSG:
            # 对端节点对本地发出的消息予以确认
            print "Message to [%s] pktnum %s is acknowledged."%(result['user'],result['additional'])

def update_contact(addr,user,host,is_absent,nick):
    """ 辅助函数。更新一个用户的状态。 """
    if addr in shared_data.contacts:
        del shared_data.contacts[addr]
    shared_data.contacts[addr]={
            "user":user,
            "result":host,
            "is_absent":is_absent,
            "nick":nick,
            }


第三部分就是键盘响应。

def keyboard_proc():
    while True:
        try:
            ln = raw_input()
            args = ln.split()
            try:
                cmd = args[0]
            except IndexError:
                continue
            if cmd == "join":
                # join命令发出加入网络的数据报
                target = BROADCAST_ADDR
                request = pdu.make_pdu(
                        1,
                        shared_data.cur_pkt_no,
                        SELF_USER,
                        SELF_HOST,
                        constants.IPMSG_BR_ENTRY,
                        shared_data.nick,
                        )
                shared_data.cur_pkt_no+=1
                s.sendto(request,target)
            if cmd == "send":
                # 给某个邻居发出消息
                user = args[1]
                msg = " ".join(args[2:])
                try:
                    target = [addr
                            for addr,info
                            in shared_data.contacts.iteritems()
                            if info['user']==user
                            ][0]
                except IndexError:
                    print "No such user"
                    continue
                request = pdu.make_pdu(
                        1,
                        shared_data.cur_pkt_no,
                        SELF_USER,
                        SELF_HOST,
                        constants.IPMSG_SENDMSG | constants.IPMSG_SENDCHECKOPT,
                        msg,
                        )
                shared_data.cur_pkt_no+=1
                s.sendto(request,target)
            if cmd == "chnick":
                # 修改昵称
                shared_data.nick = args[1]
                target = BROADCAST_ADDR
                request = pdu.make_pdu(
                        1,
                        shared_data.cur_pkt_no,
                        SELF_USER,
                        SELF_HOST,
                        constants.IPMSG_BR_ABSENCE,
                        shared_data.nick,
                        )
                shared_data.cur_pkt_no+=1
                s.sendto(request,target)
            if cmd == "readmsg":
                # 阅读加信封的消息;回复确认
                try:
                    msg,user,packet_num,addr = shared_data.readlist.pop(0)
                except IndexError:
                    print "No more message."
                    continue
                print "Message from [%s]:"%user
                print "[%s]"%msg
                target = addr
                request = pdu.make_pdu(
                        1,
                        shared_data.cur_pkt_no,
                        SELF_USER,
                        SELF_HOST,
                        constants.IPMSG_READMSG,
                        shared_data.nick,
                        )
                shared_data.cur_pkt_no+=1
                s.sendto(request,target)

        except KeyboardInterrupt:
            break
        except EOFError:
            break

# 创建接收线程
receiver_thread = threading.Thread(target=receiver_proc)
receiver_thread.daemon=True
receiver_thread.start()

keyboard_proc()


就这样,190多行代码实现了主要流程。这只是为了证明我对IP Messenger协议的理解没有重大偏差。实践证明这个程序可以和现有的GNOME2 IP Messenger实现进行简单通信。

我想,这就是典型的spaghetti code吧。一共就一个函数(好吧,两个,不,三个,有一个辅助的,还有一个解析数据报文用)。所有的功能串行在一起。

接下来的一步,就是对这个代码进行“切分“了,或者叫“模块化”了。
分享到:
评论

相关推荐

    飞鸽的java实现版

    飞鸽的Java实现版是一款基于P2P技术的文字聊天应用,它允许用户通过网络进行即时通讯,无需中间服务器作为信息中转。这个实现版本利用了Java编程语言的强大功能,为开发者提供了一个跨平台的解决方案,因为Java有...

    飞鸽源代码 C++实现

    飞鸽源代码是C++实现的一种通信软件的底层技术,它揭示了软件如何通过网络进行信息传输和交互。C++是一种强大的、通用的编程语言,以其面向对象的特性、高效性能和广泛的库支持而闻名。在飞鸽源代码中,我们可以深入...

    C#实现的飞鸽

    在这个场景下,"C#实现的飞鸽"是指使用C#编程语言开发的一个类似飞鸽传书的点对点(P2P)通信系统。下面我们将深入探讨C#语言在实现这样一个系统时涉及的关键知识点。 首先,我们要理解C#的基础特性。C#是Microsoft...

    Java实现I飞鸽传书

    在这个场景下,"Java实现I飞鸽传书"意味着我们要用Java编程语言来编写一个程序或者库,使得Java应用能够与PetaClone进行交互,实现数据的传输和同步功能。 首先,我们需要了解飞鸽传书(PetaClone)的基本原理。...

    飞鸽传书:点对点的传输工具,很方便

    2. **局域网内使用**:该软件设计之初就定位为局域网内的通信工具,因此在同一个局域网环境下,无论距离多远,只要两台电脑都安装了飞鸽传书,就能实现快速传输。 3. **实时聊天与文件共享**:除了基础的文件传输,...

    JAVA局域网飞鸽传书软件设计与实现(源代码+论文) new

    JAVA局域网飞鸽传书软件设计与实现(源代码+论文) new JAVA局域网飞鸽传书软件设计与实现(源代码+论文) new JAVA局域网飞鸽传书软件设计与实现(源代码+论文) new JAVA局域网飞鸽传书软件设计与实现(源代码+论文) new ...

    飞鸽源码vc6.0实现

    【飞鸽源码vc6.0实现】是一个关于在Microsoft Visual C++ 6.0(简称VC6.0)环境下编译和理解飞鸽(IPMsg)即时通讯软件源代码的主题。飞鸽是一款广泛使用的局域网即时通信工具,支持文字、文件、截图等多种通信功能...

    飞鸽传书协议,客户端,服务器端程序

    "XEIM 飞鸽传书传输协议.rar"文件很可能包含了飞鸽传书的协议文档或实现,详细阐述了数据在客户端和服务器之间如何封装、解封装以及传输的流程。这有助于理解飞鸽传书的底层工作机制,如数据加密方式、数据包结构、...

    2-feig.tar.gz_Qt 文件传输_qt 飞鸽_qt飞鸽传书_飞鸽_飞鸽 qt

    本文将深入探讨一个利用Qt框架实现的飞鸽传书系统,它不仅能够进行文件传输,还支持聊天功能,为用户提供了一种便捷的本地网络通信解决方案。 首先,我们要理解Qt是什么。Qt是一个跨平台的C++图形用户界面应用程序...

    局域网飞鸽 可以实现通信

    飞鸽程序在两台或多台设备之间建立连接,通过共享IP地址和端口号来实现文件交换和文本聊天。 1. **文件传输**:飞鸽通常支持拖放操作,用户可以方便地将文件或文件夹从一台计算机拖到另一台。它会将大文件分割成小...

    飞鸽 飞鸽 飞鸽 飞鸽

    它能够帮助用户在各种场景下实现便捷、安全和高效的沟通。无论是日常社交、教育学习,还是企业协作,飞鸽都为用户提供了无缝的信息交流和协作平台。随着技术的不断进步和用户需求的不断发展,飞鸽也将会不断地完善和...

    飞鸽传书源代码vc实现

    《飞鸽传书源代码VC实现深度解析》 在IT领域,源代码是理解软件运行机制的钥匙,尤其是对于网络聊天编程的学习者来说,能够深入分析一款成熟的通讯软件如“飞鸽传书”的源代码,无疑是提升技能的有效途径。本文将...

    飞鸽 飞鸽 飞鸽

    飞鸽 飞鸽 飞鸽飞鸽 飞鸽 飞鸽

    c#编写飞鸽传书(实现类似短信的功能)

    在本文中,我们将深入探讨如何使用C#编程语言来实现一个类似于短信功能的应用程序,我们将其命名为“飞鸽传书”。这个项目的核心目标是创建一个简单、高效的通信平台,允许用户发送和接收文本消息,就像传统的手机...

    飞鸽局域网内文件传送最方便

    标题“飞鸽局域网内文件传送最方便”所指的是使用“飞鸽”这款软件在局域网内部实现高效便捷的文件传输。飞鸽是一款专为局域网设计的文件分享和传输工具,它允许用户在同一个网络环境下快速地发送大文件、文件夹,...

    飞鸽传输软件 飞鸽飞鸽

    飞鸽软件的核心功能是通过局域网实现文件和数据的高速分享,极大地提高了工作效率,减少了依赖于传统网络共享或云存储服务的繁琐步骤。 首先,我们来深入了解一下飞鸽软件的工作原理。飞鸽采用点对点(P2P)技术,...

    飞鸽短信平台 API接口实现 [VB6源代码]

    【飞鸽短信平台 API接口实现】是针对VB6(Visual Basic 6)开发环境的一个具体应用,用于通过编程方式与飞鸽短信平台进行交互,发送和接收短信。VB6是一种经典的面向对象的编程语言,广泛应用于Windows应用程序的...

    用Java实现的飞鸽(附源代码)

    【标题】:“用Java实现的飞鸽(附源代码)” 在这个项目中,开发者使用Java编程语言实现了一个类似于“LanMsg”的即时通讯工具,命名为“飞鸽”。这个应用程序允许用户在同一局域网内的设备之间进行消息传递和文件...

    飞鸽软件,有需要的自己下载

    一款网络基础软件,用于朋友之间飞鸽传书,是一款很实用的小软件,需要的自己试一试。

Global site tag (gtag.js) - Google Analytics