这个代码又研究了一天,代码在part3上做了大量修改,加入了很多判断,定义了关闭连接的包
写客户端的时候发现curSock.recv(1024)发生没有阻塞的情况,打印也加了,pdb调试都显示没有发生阻塞,还以为什么参数影响了阻塞。
搞了很久才在明白,只要客户端断开后会recv就会返回0,所以要加判断。同时加了一个errcount变量,当返回0的时候增加错误计数器,到达阀值后服务器端主动断开
import daemon
import threading,socket,sys,os,time
SERVER = "0.0.0.0"
PORT = 8888
def MyTalkThread(sock,num):
import struct
# import pdb
global sockectPool
global sockLock
errCount = 0
while True:
try:
curSock,useradd = sock.accept()
curSock.settimeout(30)
threadInfo = {'thrID':num,'thrUser':'','thrSock':curSock}
while True:
print "start get data"
getData = curSock.recv(1024)
if len(getData) == 0:
if errCount < 5:
time.sleep(0.5)
print "data size 0 errcount:::",errCount
errCount+=1
continue
else:
errCount = 0
break
# pdb.set_trace()
print "Thread",num,"get data"
sockLock.acquire()
print "locked"
if threadInfo not in sockectPool:
sockectPool.append(threadInfo)
try:
packageType,packageLong,packageData = struct.unpack('BB1022s',getData)
print packageType,packageLong,packageData
except:
# print "error package"
print "error package",len(getData),"::::::::::::::",getData
if errCount < 5:
print "errcount:::",errCount
sockLock.release()
errCount+=1
continue
else:
errCount = 0
break
if packageType in (100,101,102):
print "no err"
errCount = 0
if packageType == 100:
packageData = packageData[:packageLong]
sendPackage = struct.pack('BB1022s',100,len(packageData),packageData)
for ThreadInfoPool in sockectPool:
if threadInfo is not ThreadInfoPool:
ThreadInfoPool['thrSock'].send(sendPackage)
if packageType == 101:
print "get quit word"
curSock.send(struct.pack('BB1022s',101,8,'self_out'))
break
print "right free lock"
sockLock.release()
else:
if errCount < 5:
sockLock.release()
errCount+=1
continue
else:
errCount = 0
break
print "out free lock"
sockectPool.remove(threadInfo)
sockLock.release()
curSock.close()
except:
if threadInfo in sockectPool:sockectPool.remove(threadInfo)
if sockLock.locked():sockLock.release()
try:
curSock.setblocking(0)
curSock.send(struct.pack('BB1022s',101,8,'time_out'))
except:
print "send quit to cilent faril"
print "error or time out"
curSock.setblocking(1)
curSock.close()
class TalkServer(object):
def __init__(self):
self.socket = None
def run(self):
global sockectPool
global sockLock
speakWords = ''
sockectPool = []
sockLock = threading.Lock()
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((SERVER,PORT))
self.socket.listen(10)
thread_pool = []
for i in range(10):
TalkThread = threading.Thread(target=MyTalkThread,args=(self.socket,i))
TalkThread.start()
thread_pool.append(TalkThread)
if __name__ == '__main__':
myas = TalkServer()
myas.run()
下面是客户端代码,客户端开了2个线程,一个接受一个发送
#! /usr/bin/python
#import pdb
import threading,socket,sys,os,time
import struct
SERVER = "127.0.0.1"
PORT = 8888
class TalkClient(object):
def __init__(self):
self.socket = None
self.ServerTimeOut = 0
def run(self):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((SERVER,PORT))
global link
link = 1
packageType = 100
getThread = threading.Thread(target=self.getChar)
sendThread = threading.Thread(target=self.sendChar)
getThread.start()
sendThread.start()
threading.Thread.join(sendThread)
threading.Thread.join(getThread)
# time.sleep(3)
self.socket.close()
link = 0
def getChar(self):
global link
while True:
if link == 0:
break
Data = self.socket.recv(1024)
if len(Data) == 0:continue
packageType,packageLong,backChar = struct.unpack('BB1022s',Data)
if packageType == 101:
words = backChar[0:packageLong]
print words
if words == 'time_out':self.ServerTimeOut = 1
break
print backChar[0:packageLong]
def sendChar(self):
while True:
loli = raw_input()
packageType = 100
if loli == 'quit':
packageType = 101
packageLong = len(loli)
package = struct.pack('BB1022s',packageType,packageLong,loli)
if self.ServerTimeOut == 0:self.socket.send(package)
if packageType == 101:break
if __name__ == '__main__':
myas = TalkClient()
myas.run()
print "exit"
sys.exit(0)
100是普通包
101是表示断开连接的包
无论是服务器端还是客户端处理异常的逻辑好像都不怎么严谨,但是目前已经实现了聊天室的基本功能,在包结构里加一个pack一个用户名字段就好了
附注
python socket设置非阻塞用下面参数
socket.setblocking(flag)
Set blocking or non-blocking mode of the socket: if flag is 0, the socket is set to non-blocking, else to blocking mode. Initially all sockets are in blocking mode. In non-blocking mode, if a recv() call doesn’t find any data, or if a send() call can’t immediately dispose of the data, a error exception is raised; in blocking mode, the calls block until they can proceed. s.setblocking(0) is equivalent to s.settimeout(0); s.setblocking(1) is equivalent to s.settimeout(None).
Python socket类的异常一般分为以下四类:
1.socket.error I/O和通信相关的问题
2.socket.gaierror 查询地址时产生的错误
3.socket.herror 也是地址错误
4.socket.timeout 调用settimeout后会产生超时异常
阅读(2060) | 评论(0) | 转发(0) |