Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1102559
  • 博文数量: 170
  • 博客积分: 1603
  • 博客等级: 上尉
  • 技术积分: 1897
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-09 15:54
文章分类

全部博文(170)

文章存档

2016年(27)

2015年(21)

2014年(27)

2013年(21)

2012年(7)

2011年(67)

我的朋友

分类: Python/Ruby

2011-04-22 17:15:54

这个代码又研究了一天,代码在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后会产生超时异常
阅读(2005) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~