简单实现了两个客户端之间的通信,客户端发送消息,先由服务器接收,然后服务器转发到另一客户端。
该版本功能非常简陋,仅仅实现了最简单的聊天,有很多地方需要注意。
工作步骤:
- username --> socket
即可;如果客户端发送其他文本的话,会收到来自服务器的提示:“Nobody is chatting with you. Maybe the one talked with you is talking with someone else”
- To:user
服务器端代码如下:
- #!/usr/bin/python
- #coding:utf-8
- #server.py
- from socket import *
- from time import ctime
- import threading
- import re
- HOST = ‘‘
- PORT = 9999
- BUFSIZ = 1024
- ADDR = (HOST,PORT)
- tcpSerSock = socket(AF_INET,SOCK_STREAM)
- tcpSerSock.bind(ADDR)
- tcpSerSock.listen(5)
- clients = {} # username -> socket
- chatwith = {} # user1.socket -> user2.socket
- # clients字典中记录了连接的客户端的用户名和套接字的对应关系
- # chatwith字典中记录了通信双方的套接字的对应
- # messageTransform()处理客户端确定用户名之后发送的文本
- # 文本只有四种类型:
- # None
- # Quit
- # To:someone
- # 其他文本
- def messageTransform(sock,user):
- while True:
- data = sock.recv(BUFSIZ)
- if not data:
- if chatwith.has_key(sock):
- chatwith[sock].send(data)
- del chatwith[chatwith[sock]]
- del chatwith[sock]
- del clients[user]
- sock.close()
- break
- if data==‘Quit‘:
- sock.send(data)
- if chatwith.has_key(sock):
- data = ‘%s.‘ % data
- chatwith[sock].send(data)
- del chatwith[chatwith[sock]]
- del chatwith[sock]
- del clients[user]
- sock.close()
- break
- elif re.match(‘^To:.+‘, data) is not None:
- data = data[3:]
- if clients.has_key(data):
- if data==user:
- sock.send(‘Please don\‘t try to talk with yourself.‘)
- else:
- chatwith[sock] = clients[data]
- chatwith[clients[data]] = sock
- else:
- sock.send(‘the user %s is not exist‘ % data)
- else:
- if chatwith.has_key(sock):
- chatwith[sock].send(‘[%s] %s: (%s)‘ % (ctime(),user,data))
- else:
- sock.send(‘Nobody is chating with you. Maybe the one talked with you is talking with someone else‘)
- # 每个客户端连接之后,都会启动一个新线程
- # 连接成功后需要输入用户名
- # 输入的用户名可能会:
- # 已存在
- # (客户端直接输入ctrl+c退出)
- # 合法用户名
- def connectThread(sock,test): # client‘s socket
- user = None
- while True: # receive the username
- username = sock.recv(BUFSIZ)
- if not username: # the client logout without input a name
- print(‘The client logout without input a name‘)
- break
- if clients.has_key(username): # username existed
- sock.send(‘Reuse‘)
- else: # correct username
- sock.send(‘OK‘)
- clients[username] = sock # username -> socket
- user = username
- break
- if not user:
- sock.close()
- return
- print(‘The username is: %s‘ % user)
- # get the correct username
- messageTransform(sock,user)
- if __name__==‘__main__‘:
- while True:
- print(‘...WAITING FOR CONNECTION‘)
- tcpCliSock, addr = tcpSerSock.accept()
- print(‘CONNECTED FROM: ‘, addr)
- chat = threading.Thread(target = connectThread, args = (tcpCliSock,None))
- chat.start()
客户端代码如下:
- #!/usr/bin/python
- #coding:utf-8
- #client.py
- from socket import *
- from time import ctime
- # from termios import tcflush,TCIFLUSH
- import threading
- import sys
- HOST = ‘127.0.0.1‘
- PORT = 9999
- BUFSIZ = 1024
- ADDR = (HOST,PORT)
- tcpCliSock = socket(AF_INET,SOCK_STREAM)
- tcpCliSock.connect(ADDR)
- ‘‘‘
- 因为每个客户端接收消息和发送消息是相互独立的,
- 所以这里将两者分开,开启两个线程处理
- ‘‘‘
- def Send(sock,test):
- while True:
- try:
- data = raw_input()
- sock.send(data)
- if data==‘Quit‘:
- break
- except KeyboardInterrupt:
- sock.send(‘Quit‘)
- break
- def Recv(sock,test):
- while True:
- data = sock.recv(BUFSIZ)
- if data==‘Quit.‘:
- print(‘He/She logout‘)
- continue
- if data==‘Quit‘:
- break
- print(‘ %s‘ % data)
- if __name__==‘__main__‘:
- print(‘Successful connection‘)
- while True:
- username = raw_input(‘Your name(press only Enter to quit): ‘)
- tcpCliSock.send(username)
- if not username:
- break
- # username is not None
- response = tcpCliSock.recv(BUFSIZ)
- if response==‘Reuse‘:
- print(‘The name is reuse, please set a new one‘)
- continue
- else:
- print(‘Welcome!‘)
- break
- if not username:
- tcpCliSock.close()
- recvMessage = threading.Thread(target = Recv, args = (tcpCliSock,None))
- sendMessage = threading.Thread(target = Send, args = (tcpCliSock,None))
- sendMessage.start()
- recvMessage.start()
- sendMessage.join()
- recvMessage.join()
总结:
功能简陋,后续会有所改进。这里还有很多地方需要注意。
比如说两个客户端A成功连接后,和客户端B聊天。A发送消息时直接输入
退出程序(
- ctrl+c
线程会结束),我将这种情况模拟成A发送
- sendMessage
登出。服务器接收到A登出信息之后,会回发一个
- Quit
给A,A成功登出(
- Quit
线程结束)。此外如果A和B建立了聊天关系,就要接触这个关系,服务器发送
- recvMessage
给B,B会继续接收信息,但是服务器端的
- Quit.
字典中已经不存在
- chatwith
关系。
- A.socket --> B.socket
但是还有很多没有解决的问题,比如说客户端A并没有输入信息,直接点击关闭按钮退出,就会发生异常(与之聊天的B客户端会崩溃)。
如果当前存在 A-->B的聊天关系,这时有一个C登录,并且确定了C-->A的聊天关系,那么A会和C聊天,这时客户端B就会被挂起。
主要的问题还是在于客户端非正常登出时的应对,目前解决了一部分问题,但是应该还有不少缺陷。
来源: http://www.bubuko.com/infodetail-2421967.html