聊天,服务器的一些套接字协议定义,绑定端口,监听,主循环一直等待着客户端的连接。然后是一个发送和接收消息的方法。
客户端主要加了界面化,也要定义套接字协议,然后向服务器连接,连接成功就可以互相通信了。
先看服务器server.py
import socket
import MyTools
class Server(object):
def __init__(self, host, port, lis):
self.tcpSerSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcpSerSock.bind((host, port))
self.tcpSerSock.listen(lis)
self.tcpCliSock = []
self.namelist = []
self.locker = MyTools.MyLock()
self.running = True
self.__mainloop()
self.tcpSerSock.close()
def __mainloop(self):
while self.running:
tcpCliSock, addr = self.tcpSerSock.accept()
self.locker.lock()
self.tcpCliSock.append(tcpCliSock)
self.locker.unlock()
MyTools.MyThread(self.__recv, (tcpCliSock,)).start()
def __recv(self, tcpCliSock):
BUFSIZ = 1024
name = ""
while self.running:
try:
data = tcpCliSock.recv(BUFSIZ)
except:
break
if not data:
break
if not name:
name = data
data = " ".join((name, "\n+\n"))
self.__send(data, tcpCliSock, True)
self.namelist.append(name)
else:
data = " ".join((name, MyTools.clock(), "\n", data))
self.__send(data, tcpCliSock, False)
self.locker.lock()
self.tcpCliSock.remove(tcpCliSock)
self.locker.unlock()
tcpCliSock.close()
def __send(self, data, tcpCliSock, bFirst):
self.locker.lock()
if bFirst:
tcpCliSock.send(data + str(self.namelist))
for eachSock in self.tcpCliSock:
if eachSock != tcpCliSock:
eachSock.send(data)
self.locker.unlock()
def main():
host = ""
port = 8288
lis = 5
Server(host, port, lis)
if __name__ == "__main__":
main()
主函数中定义了一些如上所说的端口,IP变量。然后就直接调用类server来初始化服务器的一些定义,其中,self.tcpCliSock是存放客户端的地址信息,self.namelist是存放用户昵称信息,self.locker是指当前的时间(会在另一个类使用time模块中调用),接着就是到了主循环,服务器一直等待着客户端的连接,tcpCliSock和addr将以元组的形式来获取客户端的ip和port,并且把ip存放到self.tcpCliSock列表里。然后会启用一个线程来接收客户端的信息。.定义__recv()是接收信息的方法,tcpCliSock.recv(BUFSIZ)是socket中内建的接收方法,没有信息则跳出循环,没有名字则把传来的信息当做名字使用(因为客户端发送的第一个信息就是用户输入的昵称)把data经过join方法的处理后调用__send方法,把名字存放到self.namelist列表中,下面这个else语句才是真正的接收到的信息的处理,从里面很容易看出信息在界面上的样式应该是名字+空格+当前时间,下一行是信息,就调用定义的__send方法来把信息发送给客户端。而且每一次服务器都要移除下当前的客户端地址,服务器同一个时刻只能和一个客户端交互(当然是执行完__send方法后)。__send方法中,服务器会对存在self.tcpCliSock列表中的客户端地址进行发送消息。
客户端 client.py
#coding:UTF-8
import socket
import wx
import Tkinter
import sys
import tkMessageBox
import MyTools
class Client(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, size=(500, 400))
def InitUI(self):
panel = wx.Panel(self, -1)
vbox = wx.BoxSizer(wx.VERTICAL)
hbox1 = wx.BoxSizer(wx.HORIZONTAL)
str1 = wx.StaticText(panel, label='host:')
hbox1.Add(str1, flag=wx.RIGHT, border=8)
self.ip = wx.TextCtrl(panel, -1, 'localhost', size=(175, -1))
hbox1.Add(self.ip, proportion=1)
vbox.Add(hbox1, flag=wx.ALL | wx.LEFT | wx.RIGHT | wx.TOP, border=10)
hbox2 = wx.BoxSizer(wx.HORIZONTAL)
str2 = wx.StaticText(panel, label='port:')
self.port = wx.TextCtrl(panel, -1, '8288', size=(175, -1))
hbox2.Add(str2, flag=wx.RIGHT, border=8)
hbox2.Add(self.port, proportion=1)
vbox.Add(hbox2, flag=wx.ALL | wx.LEFT | wx.RIGHT | wx.TOP, border=10)
hbox4 = wx.BoxSizer(wx.HORIZONTAL)
str4 = wx.StaticText(panel, label='User:')
self.user = wx.TextCtrl(panel, -1, size=(175, -1))
hbox4.Add(str4, flag=wx.RIGHT, border=8)
hbox4.Add(self.user, proportion=1)
vbox.Add(hbox4, flag=wx.ALL | wx.LEFT | wx.RIGHT | wx.TOP, border=10)
hbox3 = wx.BoxSizer(wx.HORIZONTAL)
button1 = wx.Button(panel, label='Login', size=(70, 30))
button2 = wx.Button(panel, label='Close', size=(70, 30))
self.Bind(wx.EVT_BUTTON, self.__LoginRoom, button1)
self.Bind(wx.EVT_BUTTON, self.__close, button2)
hbox3.Add(button1)
hbox3.Add(button2)
vbox.Add(hbox3, flag=wx.ALIGN_LEFT | wx.LEFT, border=10)
panel.SetSizer(vbox)
def __LoginRoom(self, ev=None):
host = self.ip.GetValue()
if not host:
tkMessageBox.showwarning("check", "host is invalid")
return
try:
port = int(self.port.GetValue())
except (ValueError, TypeError):
tkMessageBox.showwarning("check", "port is invalid")
return
self.name = self.user.GetValue()
if not self.name:
tkMessageBox.showwarning("check", "please write your name")
return
connected = False
try:
self.tcpCliSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcpCliSock.connect((host, port))
self.tcpCliSock.send(self.name)
connected = True
except:
self.tcpCliSock.close()
tkMessageBox.showerror("error", "can not connect to server")
del self.ip, self.port, self.user
if connected:
self.chatwindow()
def chatwindow(self, ev=None):
self.locker = MyTools.MyLock()
self.top = Tkinter.Tk()
self.top.title("Client")
self.topframe = Tkinter.Frame(self.top)
self.leftframe = Tkinter.Frame(self.topframe)
self.chatframe = Tkinter.Frame(self.leftframe)
self.scrollbarl = Tkinter.Scrollbar(self.chatframe)
self.scrollbarl.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
self.listboxl = Tkinter.Listbox(self.chatframe, height=15, width=50, yscrollcommand=self.scrollbarl.set)
self.scrollbarl.config(command=self.listboxl.yview)
self.listboxl.pack(side=Tkinter.LEFT, fill=Tkinter.BOTH)
self.chatframe.pack()
self.cwd = Tkinter.StringVar(self.leftframe)
self.entry = Tkinter.Entry(self.leftframe, width=50, textvariable=self.cwd)
self.entry.bind("<Return>", self.__send)
self.entry.pack()
self.buttonframe = Tkinter.Frame(self.leftframe)
self.button1 = Tkinter.Button(self.buttonframe, text="send", command=self.__send)
self.button3 = Tkinter.Button(self.buttonframe, text="close", command=self.__close)
self.button1.pack(side=Tkinter.LEFT)
self.button3.pack(side=Tkinter.LEFT)
self.buttonframe.pack()
self.leftframe.pack(side=Tkinter.LEFT)
self.rightframe = Tkinter.Frame(self.topframe)
self.scrollbarr = Tkinter.Scrollbar(self.rightframe)
self.scrollbarr.pack(side=Tkinter.RIGHT, fill=Tkinter.Y)
self.listboxr = Tkinter.Listbox(self.rightframe, height=18, width=10, yscrollcommand=self.scrollbarr.set)
self.listboxr.insert(Tkinter.END, self.name)
self.scrollbarr.config(command=self.listboxr.yview)
self.listboxr.pack(side=Tkinter.LEFT, fill=Tkinter.BOTH)
self.rightframe.pack(side=Tkinter.LEFT)
self.topframe.pack()
self.thread = MyTools.MyThread(self.__recv, ())
self.thread.setDaemon(True)
self.thread.start()
self.top.mainloop()
def __close(self, ev=None):
sys.exit()
def __send(self, ev=None):
data = self.cwd.get()
self.cwd.set("")
if data.strip():
self.tcpCliSock.send(data)
info = " ".join((self.name, MyTools.clock()))
data = "".join((" " * 4, data))
self.locker.lock()
self.listboxl.insert(Tkinter.END, info)
self.listboxl.insert(Tkinter.END, data)
self.locker.unlock()
else:
tkMessageBox.showinfo("info", "can not send blank string")
def __recv(self):
BUFSIZ = 1024
while True:
try:
data = self.tcpCliSock.recv(BUFSIZ)
except:
break
if not data:
break
else:
index = data.find("\n")
if data[index:index+3] == "\n+\n":
names = data[index+3:]
if names:
namelist = eval(names)
for name in namelist:
self.listboxr.insert(Tkinter.END, name)
else:
name = data[:index-1]
self.listboxr.insert(Tkinter.END, name)
elif data[index:index+3] == "\n-\n":
name = data[:index-1]
for i in range(self.listboxr.size()):
if self.listboxr.get(i) == name:
self.listboxr.delete(i, i+1)
break
else:
info = data[:index].strip()
data = "".join((" " * 4, data[index+1:].strip()))
self.locker.lock()
self.listboxl.insert(Tkinter.END, info)
self.listboxl.insert(Tkinter.END, data)
self.locker.unlock()
self.tcpCliSock.close()
tkMessageBox.showerror("error", "disconnect to server")
def main():
app = wx.App()
frame = Client('Simple Editor')
frame.InitUI()
frame.Show()
app.MainLoop()
if __name__ == "__main__":
main()
客户端中主要加入了界面化的处理,第一个界面是登陆界面,主要用于输入ip ,port ,username,还需要两个按钮Login和Close,两个按钮分别绑定了__LoginRoom和__close方法,__LoginRoom方法中主要是获取界面中输入的信息而用来判断是否输入或输错信息,然后建立客户端的套接字协议,且用到了捕捉异常,如果成功连接到服务器,就把输入的username发送给服务器,如果连接失败,就会捕获到异常,使客户端关闭连接请求。连接成功后会进入到第二个聊天界面,聊天界面主要是四个框架,显示消息文本框,输入消息文本框,用户名显示文本框和两个按钮,其中显示消息的文本框和显示用户名的文本框都绑定了滚动控件,两个按钮send和close也绑定了对应的方法,方法__send获取了输入的信息,在对信息进行处理后,把信息发送给服务器,并且在自己的显示信息文本框里显示自己输入的信息。__recv方法用于接收服务器发送来的消息,根据索引把对方的名字,当前时间和信息进行归纳或显示在自己的聊天界面里。
最后一个文件是关于线程的:
import time
import Queue
import threading
def clock():
strtime = time.ctime()
index = strtime.index(":")
return strtime[index-2:index+6]
class MyThread(threading.Thread):
def __init__(self, func, args):
threading.Thread.__init__(self)
self.func = func
self.args = args
def run(self):
apply(self.func, self.args)
class MyLock(object):
def __init__(self):
self.queue = Queue.Queue(1)
def lock(self):
self.queue.put("", True)
def unlock(self):
self.queue.get(True)