Python网络编程 (三)使用select.select()实现聊天服务器

第一步, 实现通用的send()和receive()函数:

send函数定义通过cPicle.dumps()将需要发送的数据序列化,然后通过socket.htonl()方法将序列化后的数据长度转化为网络字节序格式,以便于底层传输,再将网络字节序格式的长度打包为‘L‘类型的C struct, 最后发送打包后的长度以及序列化后的数据

receive函数即是send反向过程,先接收到打包后的长度,将其解包,然后再主机序列化,所有数据接收完成以后,返回解除序列化后的原始数据。

 1 def send(channel, *args):
 2     data = cPickle.dumps(*args)
 3     htonl_size = socket.htonl(len(data))
 4     size = struct.pack("L", htonl_size)
 5     channel.send(size)
 6     channel.send(data)
 7
 8 def receive(channel):
 9      recv_size = struct.calsize("L")
10      data_size = channel.recv(recv_size) # receive size‘s value
11      try:
12           size = socket.ntohl(struct.unpack("L", data_size)[0])
13      except struct.error, e:
14           return ‘‘
15      data = ‘‘
16      while len(data) < data_size:
17           data = channel.recv(data_size - len(data))
18      return cPickle.loads(data)

第二步,创建ChatServer类:

聊天室服务器需要能做到: 1,记录连接数  2,记录连接的客户端地址以及名称映射 ,需要时返回名称地址 3,重用地址 4,检测键盘中断 5,处理输入及请求

先实现1,2,3,4点:

 1 class ChatServer(object):
 2     def __init__(self, port, backlog=5):
 3         self.clients = " # record client number
 4         self.clientmap = {} # client address and name‘s mapping
 5         self.outputs = [] # socket output objects‘ list
 6         self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 7         self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 8         self.server.bind((SERVER_HOST, port)
 9         self.server.listen(backlog)
10         signal.signal(signal.SIGINT, self.sighandler)
11
12     def sighandler(self, signum, frame):
13         print "Shutting down server..."
14         for output in outputs:
15              output.close(0)
16         self.server.close()
17
18     def get_client_name(self, client):
19         info = self.clientmap[client]
20         host, name = info[0][0], info[1]
21         return ‘@‘.join((name, host))
22    

第5点处理输入及请求分几种情况,1处理客户端接入并通知其他客户端,2处理客户端输入信息并转发给其他客户端,处理标准输入, 这里丢弃,3处理异常

 1     def run(self):
 2         inputs = [self.server, sys.stdin] #inputs for select.select() first arg
 3         self.outputs = [] # define outputs for select.select(), second arg
 4         running = True
 5         while running:
 6             try:
 7                 readable, writeable, exceptional =  8                     select.selcet(inputs, self.outputs, [])
 9             except select.error, e:
10                 break
11
12             for sock in readable:
13                 if sock == self.server:
14                     client, address = self.server.accept()
15                     print "Chat server: got connection %d from %s" % \
16                         (client.fileno, address)
17                     cname = receive(client).spilt(‘NAME: ‘)[1]
18                     self.clients += 1
19                     send(client, "CLIENT: " + str(address[0])
20                     inputs.append(client)
21                     self.clientmap[client] = (address, cname)
22                     # send joining information to other clients
23                     msg = ‘\n (Connected: New client (%d) from %s" % 24                          (self.clients, self.get_client_name(client))‘
25                     for output in self.outputs:
26                         send(output, msg)
27                     self.outputs.append(client)
28
29                  elif sock == sys.stdin:
30                      junk = sys.stdin.readable()
31                      running = False
32                  else:
33                      try:
34                          data = receive(sock)
35                          if data:
36                              msg = ‘\n#[‘ + self.get_client_name(sock) + 37                                  ‘]>>‘ + data
38                              for output in self.outputs:
39                                  if output != sock:
40                                      send(output.msg)
41                          else:
42                              print "Chat server: %d hung up" % sock.fileno()
43                              self.clients -= 1
44                              sock.close()
45                              inputs.remove(sock)
46                              self.outputs.remove(sock)
47                              msg = ‘\n(now hung up: client from %s)‘ % \
48                                   sef.get_client_name(sock)
49                              for output in self.outputs:
50                                  send(output, msg)
51                     except socket.error, e:
52                         inputs.remove(sock)
53                         self.outputs.remove(sock)
54
55             self.server.close()
时间: 2024-11-08 21:21:05

Python网络编程 (三)使用select.select()实现聊天服务器的相关文章

python网络编程——socket进阶篇(select/poll/epoll)

原 生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收数据(调用recv)时也是阻塞的.原生 socket服务端在同一时刻只能处理一个客户端请求,即服务端不能同时与多个客户端进行通信,实现并发,导致服务端资源闲置(此时服务端只占据 I/O,CPU空闲). 现在的需求是:我们要让多个客户端连接至服务器端,而且服务器端需要处理来自多个客户端请求.很明显,原生socket实现不了这种需求,此时我们该采用什么方式来处理呢? 解决方法:采用I/O多路复

python 网络编程:socket和select实现伪并发

上节地址:Python网络编程:socket 先补充点内容: 一.send和sendall区别 send,sendall ret = send('safagsgdsegsdgew') #send 发送完成后会有一个返回值,告知发送了多少,并不一定会把数据全部发送过去. sendall:内部调用send,将数据全部发送完为止. 因此我们使用时最好使用sendall 二.粘包 粘包问题需要理解recv()的使用,我们定义接收值的时候会写recv(1024)表示一次接收1024字节,但是有时候接收的数

python网络编程三次握手和四次挥手

TCP是因特网中的传输层协议,使用三次握手协议建立连接.当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认.这种建立连接的方法可以防止产生错误的连接.[1] TCP三次握手的过程如下: 客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态. 服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态. 客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进

【学习笔记】Python网络编程(三)利用socket模拟ssh协议

上代码,server端: import socket,os s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) host = '' port = 1051 s.bind((host,port)) s.listen(4) while 1:     conn,addr = s.accept()     while 1:         data = conn.recv(1024)         if not data:break        

嵌入式 Linux网络编程(四)——Select机制

嵌入式 Linux网络编程(四)--Select机制 一.select工作机制 poll和select,都是基于内核函数sys_poll实现的,不同在于在linux中select是从BSD Unix系统继承而来,poll则是从SYSTEM V Unix系统继承而来,因此两种方式相差不大.poll函数没有最大文件描述符数量的限制.poll和 select与一样,大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,开销随着文件描述符数量的增加而线性增大. select需要驱动程序的支持,驱动

Python 网络编程(二)

Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端 服务端 1 2 3 4 5 6 7 8

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python 网络编程(一)

Python 网络编程 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者. socket和file的区别: file模块是针对某个指定文件进行[打开][读写][关闭] socket模块是针对 服务器端 和 客户端Socket 进行[打开][读写][关闭] socket服务端和客户端的网

python 网络编程:socket

在学习socket之前,我们先复习下相关的网络知识. OSI七层模型:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层.OSI七层模型是由国际标准化组织ISO定义的网络的基本结构,不仅包括一些概念和结构,还包括一系列的协议. TCP/IP四层模型:既然有OSI七层模型,为什么我们还要定义一个TCP/IP的四层模型呢,那是因为OSI七层模型对应面过于宽泛,很多概念实现不了,也没必要实现,因此,实际生产中广泛应用的是TCP/IP四层结构,他们的对应关系如下表: TCP/IP OSI 应用层