python socket threading 感受 疑问 总结

本文从WordPress迁移而来, 查看全部WordPress迁移文章

win7 python2.7

socket编程基础

这是个实验程序,主要是测试服务器。前几天写了个图形界面聊天软件,可以一对一通信,然后现在希望改成一对多,另外支持客户端间通信,随即思考怎么改

服务器的设计

大体的思路就是,主线程是图形界面那一块,然后服务器在后台一直监听客户的连接,所以,这里已经创建了一个后台线程。每当有客户到达,就单独去处理这个客户,所以这里又创建了一个线程。层次就是 主线程->监听线程->多个线程,每个对应一个客户;除主线程外,其他都设置为后台线程

1
2
while True:
connection , clientaddress = s.accept()

后来想到一个问题,如果在图形界面加上一个功能,设置两个按钮“启动监听”,“停止监听”,可以启动和停止监听线程。这样的话,要实现的就是,监听线程能随时挂起,唤醒,重复使用。

这里遇到了个问题,threading模块并没有提供一个函数,可以将线程挂起,而且这个线程有点特别,里面的accept是阻塞的
虽然threading的condition和event好像可以做到这个功能,但因为accept是阻塞的,所以不行

另外,一开始是以面向对象的方式来写代码,封装在类中,但是在响应一个客户的时候,出了些问题,主要是对python的类还不是很熟悉,对于python的函数参数传递还不够熟悉,对python的可变对象,不可变对象还不够熟悉。另外,线程函数就是类里面的成员函数,这样的做法,不知道在python里面是不是一个好的方法

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
# -*- coding: utf-8 -*-
import socket
import threading
import time
import sys
import random

def recvFunc(con,caddr):
# 线程函数,接受客户发来的信息
# 将信息输出到 屏幕 和 文件 上
print caddr,' connected ......'
clientFILE = open(str(caddr)+'-data.txt','w')
while True:
data = con.recv(1024)
# 这里是个疑问
# if not data: break
# 是不行的
# 即如果客户端都断开连接了,服务器还是会在接受信息
# 写成 not len(data) 是可以的,但如果客户端有意发送一个空串过来
# 服务器会认为它已经离开
if not len(data):
break
clientFILE.write(time.ctime()+str(caddr)+' : '+data+'\n')
print str(caddr)+time.ctime()+data
print caddr , ' exit '
logFILE.write(time.ctime()+str(caddr)+' exit '+'\n')

# 客户断开连接了,在clientConnection中删除对应的项
temp = []
for i in range(len(clientConnection)):
if clientConnection[i][1] == caddr:
temp = clientConnection[i]
clientConnection.pop(i)
break
print 'clientConnection len :',len(clientConnection)
print temp
clientFILE.close()
con.close()



def listenFunc():
s.bind(addr)
s.listen(10)
while True:
print 'waiting for connection ......'
connection , clientaddress = s.accept()
# 每当有客户连接进来,就创建一个线程temp去响应
temp = threading.Thread(target=recvFunc,
args=(connection,clientaddress),
name='clientThread'+str(len(clientConnection)))
temp.setDaemon(True)
logFILE.write(time.ctime()+str(clientaddress)+'\n')
clientConnection.append([connection,clientaddress])
temp.start()
time.sleep(0.5)
s.close()


################### about port #####################
# 测试过程中可能出错,程序异常退出后,端口还没释放
# 所以每次启动的时候,都随机random一个端口去bind
# 因为每次都random一个端口,所以客户端连接的时候,也需要这次服务器的端口是什么
# 把每次random的端口写在一个文件中,客户端每次启动的时候先去读文件,得到服务器本次的端口,然后去connect
tempFILE = open('PORT.txt','w')
TEMPPORT = random.randint(8001,10000)
tempFILE.write(str(TEMPPORT))
tempFILE.close()
################### about port #####################

################### socket #########################
# 创建服务器的socket
port = TEMPPORT
host = 'localhost'
ip = socket.gethostbyname('localhost')
addr = (ip,port)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
################## socket ##########################

################## thread ##########################
# 监听线程,线程函数是 listenFunc
listenThread = threading.Thread(target=listenFunc,args=(),name="listtenThread")
# 监听线程设置为后台(守护)线程
listenThread.setDaemon(True)
# 这个列表是为了记录当前当前在线的客户有哪些,每成功连接一个客户,就把信息放进这个列表
# 客户断开连接的时候就删除列表中对应的项
clientConnection = []
################## thread #########################

################## file ##########################
# 一个测试文件,记录什么时候,哪些客户连接了,什么时候,哪么客户离开了
logFILE = open('log.txt','w')
################## file ##########################


def main():
# 启动监听线程
listenThread.start()
# 让主线程sleep(50),50s内进行测试
time.sleep(50)

if __name__ == '__main__':
main()