第三周讲稿


简单的网络通信及即时聊天
  今天是网络编程。不妨做一个即时聊天工具吧, 不过这次的程序很简陋, 只能说是一个网络对讲机罢了。
  今天我不准备去讲更底层的东西, 比如使用 Socket 来编写通信程序。不过以后, 可能就是下次我们就会涉及到。
  然而, 今天的东西相当有用。在没有必要钻牛角尖的时候 (这是大多数的情况), 今天所用到的方法几乎是第一选择。使用 XMLRPC 来进行网络编程, 确实出奇的简单。但是请注意, 千万不要被现在国内的风气给误导了, 其实使用简单的工具来进行编程并不愚蠢, 相反是你聪明的表现。而且, 复杂的应用通常是用最简单的方法建立的, 如果妄图使用石器时代的工具来建造高楼大厦, 那几乎是不可能的。
  下面先来个服务器。
  from SimpleXMLRPCServer import SimpleXMLRPCServer
  def foo():
    return "Hello world!"
  server = SimpleXMLRPCServer( ("localhost", 8000) )
  server.register_function(foo)
  server.serve_forever()
  用 IE 来访问 http://localhost:8000 看看我们得到了什么? 是一个出错页面, 不错, 这表示我们的服务器工作正常, 只是访问的方法不对而已。
  下面, 给出这个程序的客户机程序。
  from xmlrpclib import ServerProxy
  server = ServerProxy("http://localhost:8000")
  print server.foo()
  我们会看到服务器返回了 "Hello world!"。这里 localhost 是指本机, 8000 是端口, 我们通常会指定大于 1024 的端口号。localhost 我们通常会用域名 (机器名) 或 IP 来代替, 而端口号是任意的。
  下面, 该传些东西了。比如发送一句话到服务器上去显示出来。
  from SimpleXMLRPCServer import SimpleXMLRPCServer
  def msg(s):
    print s
    return True
  server = SimpleXMLRPCServer( ("localhost", 8000) )
  server.register_function(msg)
  server.serve_forever()
  对应的客户机程序。
  from xmlrpclib import ServerProxy
  server = ServerProxy("http://localhost:8000")
  while True:
    msg = raw_input()
    server.msg(msg)
  开个玩笑, 一个即时通信工具已经写好了。两台机器互相知道 IP 或者机器名, 协商好端口号。然后打开一个服务器、将客户机指向对方的服务器就可以了。
  那么怎么将客户端和服务器合并在一起呢? 因为调用 serve_forever() 之后程序就停在那里了, 所以无法再接收用户输入的东西了。这里顺便讲一下多线程编程, 但是这个话题不在本次主题之内, 大家仅做理解就可以了。
  import thread
  from SimpleXMLRPCServer import SimpleXMLRPCServer
  from xmlrpclib import ServerProxy
  def msg(s):
    print s
    return True
  def run_server():
    my_server = SimpleXMLRPCServer( ("localhost", 8001) )
    my_server.register_function(msg)
    my_server.serve_forever()
  def run_client():
    your_server = ServerProxy("http://localhost:8002")
    while True:
      msg = raw_input()
      your_server.msg(msg)
  thread.start_new_thread( run_server, () )
  run_client()
  第二个差不多, 只是把互相的地址换了一下而已。
  import thread
  from SimpleXMLRPCServer import SimpleXMLRPCServer
  from xmlrpclib import ServerProxy
  def msg(s):
    print s
    return True
  def run_server():
    my_server = SimpleXMLRPCServer( ("localhost", 8002) )
    my_server.register_function(msg)
    my_server.serve_forever()
  def run_client():
    your_server = ServerProxy("http://localhost:8001")
    while True:
      msg = raw_input()
      your_server.msg(msg)
  thread.start_new_thread( run_server, () )
  run_client()
  这样, 一对对讲机就写好了。
  接下来就要用到我上一次讲到的图形界面的知识, 用 Tkinter 来写一个界面了。
  首先把窗体画出来。
  from Tkinter import *
  wnd = Tk()
  wnd.lab = Label(wnd, text="---")
  wnd.ent = Entry(wnd)
  wnd.btn = Button(wnd, text="send")
  wnd.lab.pack()
  wnd.ent.pack(side=LEFT)
  wnd.btn.pack(side=LEFT)
  然后把刚才的网络部分加上去。
  import thread
  from SimpleXMLRPCServer import SimpleXMLRPCServer
  from xmlrpclib import ServerProxy
  # 第一个对讲机
  my_server   = SimpleXMLRPCServer( ("localhost", 8002) )
  your_server = ServerProxy("http://localhost:8001")
  # 第二个对讲机 (需要交换一下地址)
  # my_server   = SimpleXMLRPCServer( ("localhost", 8001) )
  # your_server = ServerProxy("http://localhost:8002")
  def msg(s):
    wnd.lab.config(text=s)
    return True
  def run_server():
    my_server.register_function(msg)
    my_server.serve_forever()
  def send():
    msg = wnd.ent.get()
    wnd.ent.select_range(0, len(msg))
    your_server.msg(msg)  
  wnd.btn.config(command=send)
  thread.start_new_thread( run_server, () )
  wnd.mainloop()
  因为程序比较长, 所以另外一个终端就不写了。注意, 这里两台终端的地址是在程序中写死的。而且现在的程序只能显示最近收到的一条消息。
  ·我们可以使用 Tkinter 中的输入框来叫用户自己输入目标机器的地址和端口。
  ·我们可以使用 Tkinter 中的编辑框组件来显示多行消息。
  这个作为这次的作业。我的意思是完善这个聊天程序, 做成比较漂亮的样子。同时思考一下如何来实现一个多人聊天室。
  下周可能会涉及到 Socket, 请预习一下。