python基础教程项目五之虚拟茶话会

时间:2022-09-10 23:34:35

几乎在学习、使用任何一种编程语言的时候,关于socket的练习从来都不会少,尤其是会写一些局域网的通信的东西。所以书上的这个项目刚好可以练习一下socket编程。

这个练习的整体思路首先有一个聊天的服务器,这个服务器的功能主要是提供客户端socket的连接、存储每个客户端的连接session,处理每个连接发送的消息、解析客户端发送的数据。就这些,至于客户端方面不需要写代码,用系统的telnet工具即可。

我觉得有了上面的分析,剩下的这个程序就没有什么说的了,当然,除了那两个把socket封装的类之外。

自己使用python中的socket类尝试这个编写了一个简单的通信程序,不过不知为什么,通信中总是出现意外。这段简单的代码如下:

server.py

?
1
2
3
4
5
6
7
8
9
10
11
import socket
 
mysocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
mysocket.bind(('',8888))
mysocket.listen(5)
 
while True:
    connection,addr = mysocket.accept()
    revStr = connection.recv(1024)
    connection.send('Server:' + revStr)
    connection.close()

clinet.py

?
1
2
3
4
5
6
7
8
9
10
11
12
import socket
import time
 
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
clientsocket.connect(('',8888))
while True:
    time.sleep(2)
    clientsocket.send('hello the5fire')
    print clientsocket.recv(1024)
 
clientsocket.close()

这个程序出错的原因没有去细揪,因为python中提供了两个封装好的类来完成socket通信过程:asynchat中的async_chat和asyncore中的dispatcher以及asyncore本身。前面的类是用来处理客户端同服务器的每一次会话,后面的类主要是用来提供socket连接服务。并且将每一个socket连接都托管给前者(async_chat)来处理。

来看代码:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore
 
PORT = 5005
NAME = 'TestChat'
 
class EndSession(Exception):pass
 
class CommandHandler:
 
    def unknown(self, session, cmd):
        session.push('Unknown command: %s\r\n' % cmd)
 
    def handle(self, session, line):
        if not line.strip(): return
 
        parts = line.split(' ',1)
        cmd = parts[0]
        try: line = parts[1].strip()
        except IndexError: line = ''
 
        meth = getattr(self, 'do_'+cmd, None)
 
        try:
            meth(session, line)
        except TypeError:
            self.unknown(session,cmd)
 
class Room(CommandHandler):
 
    def __init__(self, server):
        self.server = server
        self.sessions = []
 
    def add(self, session):
        self.sessions.append(session)
 
    def remove(self, session):
        self.sessions.remove(session)
 
    def broadcast(self, line):
        for session in self.sessions:
            session.push(line)
 
    def do_logout(self, session, line):
        raise EndSession
 
class LoginRoom(Room):
 
    def add(self,session):
        Room.add(self,session)
 
        self.broadcast('Welcome to %s\r\n' % self.server.name)
 
    def unknown(self, session, cmd):
        session.push('Please log in \nUse "login"\r\n')
 
    def do_login(self, session, line):
        name = line.strip()
 
        if not name:
            session.push('Please enter a name\r\n')
        elif name in self.server.users:
            session.push('The name "%s" is taken.\r\n' % name)
            sessoin.push('Please try again.\r\n')
        else:
            session.name = name
            session.enter(self.server.main_room)
 
class ChatRoom(Room):
 
    def add(self, session):
        self.broadcast(session.name + ' has entered the room.\r\n')
        self.server.users[session.name] = session
        Room.add(self, session)
 
    def remove(self, session):
        Room.remove(self, session)
 
        self.broadcast(session.name + ' has left the room.\r\n')
 
    def do_say(self, session, line):
        self.broadcast(session.name + ': ' + line + '\r\n')
 
    def do_look(self, session, line):
        session.push('The following are in this room:\r\n')
        for other in self.sessions:
            session.push(other.name + '\r\n')
 
    def do_who(self, session, line):
        session.push('The following are logged in:\r\n')
        for name in self.server.users:
            session.push(name + '\r\n')
 
class LogoutRoom(Room):
 
    def add(self, session):
        try: del self.server.users[session.name]
        except KeyError: pass
 
class ChatSession(async_chat):
 
    def __init__(self, server, sock):
        async_chat.__init__(self,sock)
        self.server = server
        self.set_terminator('\r\n')
        self.data = []
        self.name = None
 
        self.enter(LoginRoom(server))
 
    def enter(self, room):
 
        try:
            cur = self.room
        except AttributeError:
            pass
        else: cur.remove(self)
        self.room = room
        room.add(self)
 
    def collect_incoming_data(self, data):
        self.data.append(data)
 
    def found_terminator(self):
        line = ''.join(self.data)
        self.data = []
        try: self.room.handle(self, line)
        except EndSession:
            self.handle_close()
 
    def handle_close(self):
        async_chat.handle_close(self)
        self.enter(LogoutRoom(self.server))
 
class ChatServer(dispatcher):
 
    def __init__(self, port, name):
        dispatcher.__init__(self)
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.bind(('',port))
        self.listen(5)
        self.name = name
        self.users = {}
        self.main_room = ChatRoom(self)
 
    def handle_accept(self):
        conn, addr = self.accept()
        ChatSession(self,conn)
 
if __name__ == '__main__':
    s = ChatServer(PORT, NAME)
    try: asyncore.loop()
    except KeyboardInterrupt: print

整个程序分为我一开始说的三个部分:

提供客户端的socket连接:ChatServer类。

存储每个客户端的连接session,处理每个连接发送的消息:ChatSession类,这个类的作用很简单,接受数据,判断是否有终结符,如果有调用found_terminator这个方法。

解析客户端发送的数据:就是剩下的room相关的类,这些类分别用来处理客户端发送的字符串和命令,都是继承自CommandHandler。

最终截图:

python基础教程项目五之虚拟茶话会

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.the5fire.com/python-project5-socketchat.html