建立TCP服务器步骤:建立socket对象,设置socket选项,绑定到一个端口,侦听连接。
一、建立连接。
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
二、设置选项(可以不用)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
此选项的意义是当socket关闭时,本地端用于该socket的端口号立刻就可以被重用。通常,只有经过系统定义的一段时间后,才能被重用。我在测试时,发现当服务端的socket关闭时,通过netstat查看会显示TIME_WAIT状态,若未设置此选项,会提示socket.error: [Errno 98] Address already in use。
具体选项可看socket(7)手册
三、绑定端口
s.bind('',80)
四、侦听连接
s.listen(5)
此参数指明了在服务器实际处理连接的时候,允许有多少个等待的连接在队列中等待。我在测试的时候发现,当参数为5的时候,允许连接的socket(包括已连接的和等待的)有7个,当参数为1时,有3个,其余的没有测试。。之后的连接就会显示SYN_RECV的状态。。。
服务器需考虑 到异常处理的情况,要确保服务不会因为异常而停止运行。
创建UDP服务器伪代码:
ss=socket() ss.bind() inf_loop: ss.recvfrom()/ss.sendto() ss.close()
通过syslog来记录日志
Python提供了一个syslog模块。在开始记录信息前,必须先调用openlog()来初始化syslog接口。
openlog(ident[,logopt[,facility]])
ident是一个标识符。通常它是程序的名称, 有时还包含进程ID。
日志选项是整数,包含:LOG_CONS,LOG_NDELAY,LOG_NOWAIT,LOG_PID,LOG_PERROR
工具参数用来识别产生信息的程序类型,包含:LOG_AUTH,LOG_CRON,LOG_DAEMON,LOG_KERN,LOG_LOCALx,LOG_LPR,LOG_MAIL,LOG_NEWS,LOG_USER,LOG_UUCP
初始化后,可以调用syslog()函数来实际记录一条信息。
syslog([priority,]message)
优先权如下(从高到低):LOG_EMERG,LOG_ALERT,LOG_CRIT,LOG_ERR,LOG_WARNING,LOG_NOTICE,LOG_DEBUG
各参数具体意思可查看man syslog。例子:
#!/usr/bin/env python import syslog,sys,StringIO,traceback,os def logexception(includetraceback=0): exctype,exception,exctraceback=sys.exc_info() excclass=str(exception.__class__) message=str(exception) if not includetraceback: syslog.syslog(syslog.LOG_ERR,"%s: %s" % (excclass,message)) else: excfd=StringIO.StringIO() traceback.print_exception(exctype,exception,exctraceback,None,excfd)#打印错误到excfd for line in excfd.getvalue().split('\n'): #getvalue():return whole file's contents as a string syslog.syslog(syslog.LOG_ERR,line) def initsyslog(): syslog.openlog("%s[%d]" % (os.path.basename(sys.argv[0]),os.getpid()),0,syslog.LOG_DAEMON) syslog.syslog("Started") initsyslog() try: raise RuntimeError,'Exception 1' except: logexception(0) try: raise RuntimeError,'Exception 2' except: logexception(1) syslog.syslog("I'm terminating.")
避免死锁
死锁发生在当一个服务器和客户端同时试图往一个连接上写东西和同时从一个连接上读的时候。在这些情况下,没有进程可以得到任何数据(如果它们都在读)。若它们都在写,向外的buffer会被充满。例子:
服务器代码:
#!/usr/bin/env python from socket import * import traceback HOST='' PORT=51234 ADDR=(HOST,PORT) BUF=4096 s=socket(AF_INET,SOCK_STREAM) s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) s.bind(ADDR) s.listen(1) while True: try: client,addr=s.accept() except KeyboardInterrupt: raise except: traceback.print_exc() continue try: print 'Got connection from ',client.getpeername() while True: data=client.recv(BUF) if not len(data): break client.sendall(data) except (KeyboardInterrupt,SystemExit): raise except: traceback.print_exc() try: client.close() except KeyboardInterrupt: raise except: traceback.print_exc()
客户端:
#!/usr/bin/env python from socket import * import sys HOST='localhost' PORT=51234 ADDR=(HOST,PORT) data='x'*10485760 #10MB of data s=socket(AF_INET,SOCK_STREAM) s.connect(ADDR) byteswritten=0 while byteswritten<len(data): startpos=byteswritten endpos=min(byteswritten+1024,len(data)) byteswritten+=s.send(data[startpos:endpos]) sys.stdout.write('Wrote %d bytes\r'%byteswritten) sys.stdout.flush() s.shutdown() print 'all data sent.' while 1: buf=s.recv(1024) if not len(buf): break sys.stdout.write(buf)
书上说,当启动服务器后启动客户端,客户端的10M数据永远也发送不完,因为服务器会读取开始的4KB,试图在写完后重复这个过程。但客户端在发送完所有数据之前不能进行任何读取,操作系统的传输buffer在某一点上会充满,两个进程会在send()函数停止。解决这个问题的方法有很多,最直接的是确保客户端每次执行完send()后,进行一此recv(),或是发送较少的数据。还有一种方法是用多线程。。。不过我在测试的时候,发现可以正常进行,打印除了10MB的数据,不知是什么原因。。。。