服务端:
基本思路生成socket,绑定IP端口,listen后使用select或者kqueue管理链接是否可以读写操作。获得链接后,等待客户端输入,将从客户端收到的数据原样返回给客户端。实现基本的回显操作。
服务端代码:
select模式
# coding=utf-8
__author__ = 'chenglp'
"""socket,select, kqueue学习
"""
import socket
import select
import sys
HOST = '127.0.0.1'
PORT = 8001
BUFFER_SIZE = 1024
#生成socket,绑定ip端口
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM,)
server.bind((HOST, PORT))
server.listen(5)
#创建输入监听列表
inputs = [server, sys.stdin]
#select模式
def select_mode():
running = True
while running:
try:
#select轮询inputs列表获取可以读的描述符
readable, writeable, exceptional = select.select(inputs,[],[])
except select.error as e:
break
for each in readable:
#如果是server,则accept创建链接,并将链接的描述符放入到inputs进行select监听
if each == server:
conn, addr = server.accept()
inputs.append(conn)
#如果是系统输入,则获取系统输入,停止程序
elif each == sys.stdin:
junk = sys.stdin.readline()
running = False
#如果是其它的,则为socket链接,读取链接数据,并返回读取到的数据。
else:
try:
data = each.recv(BUFFER_SIZE)
if data:
each.send(data)
if data.endswith('\r\n\r\n'):
inputs.remove(each)
each.close()
else:
inputs.remove(each)
each.close()
except socket.error as e:
inputs.remove(each)
server.close()
epoll模式:
#epoll模式
def epoll_mode():
running = True
epoll = select.epoll()
#注册epoll读
epoll.register(server.fileno(), select.EPOLLIN)
#保存链接文件描述符和链接的对应关系
conn_list = {}
while running:
try:
#开始执行epoll
events = epoll.poll(1)
except:
break
if events:
for fileno, event in events:
#如果是新链接,则accept,并将新链接注册到epoll中
if fileno == server.fileno():
conn, addr = server.accept()
conn.setblocking(0)
epoll.register(conn.fileno(), select.EPOLLIN)
conn_list[conn.fileno()] = conn
#如果是其他的可读链接,则获取到链接,recv数据,如果是\n, 则close,并从epoll中unregister掉这个文件描述符
elif event == select.EPOLLIN:
data = conn_list[fileno].recv(BUFFER_SIZE)
if data.startswith('\n'):
conn_list[fileno].close()
epoll.unregister(fileno)
del(conn_list[fileno])
else:
conn_list[fileno].send(data)
server.close()
kqueue模式:
#kqueue模式
def kqueue_mode():
running = True
kq = select.kqueue()
conn_list = {}
index = 1
#生成kevents列表,监听socket的读操作
events = [
select.kevent(server.fileno(), select.KQ_FILTER_READ,select.KQ_EV_ADD),
]
while running:
try:
#开始kqueue,如果有可执行kevent,则返回对应的kevent列表
eventlist = kq.control(events,1)
except select.error as e:
break
if eventlist:
for each in eventlist:
#如果是socket链接,则accept,将conn创建kevent放入到events进行监听,将链接放入到conn_list进行保存,key为index。
if each.ident == server.fileno():
conn, addr = server.accept()
conn_list[index] = conn
events.append(select.kevent(conn_list[index].fileno(), select.KQ_FILTER_READ, select.KQ_EV_ADD,udata=index))
index += 1
else:
try:
#如果不是socket链接,则获取到conn,然后进行读写操作。
if each.udata >= 1 and each.flags == select.KQ_EV_ADD and each.filter == select.KQ_FILTER_READ:
conn = conn_list[each.udata]
data = conn.recv(BUFFER_SIZE)
if data.startswith('\n'):
conn.close()
events.remove(select.kevent(conn_list[each.udata].fileno(), select.KQ_FILTER_READ,
select.KQ_EV_ADD,udata=each.udata))
del(conn_list[each.udata])
else:
conn.send(data)
except:
pass
server.close()
客户端:
# coding=utf-8
import socket
import select
import sys
HOST = '127.0.0.1'
PORT = 8001
BUFFER_SIZE = 1024
# 创建socket链接,链接服务器
s = socket.socket()
try:
s.connect((HOST, PORT))
except socket.error as e:
print e
#创建读写监听列表
readlist = [s, sys.stdin]
writelist = [s, ]
#输入缓存
writeCache = []
while True:
try:
#select监听获取可读可写
readable, writeabel, exceptional = select.select(readlist, writelist, [])
except select.error as e:
break
for each in readable:
#如果是系统输入,则获取输入信息放到输入缓存里
if each == sys.stdin:
inputs = sys.stdin.readline()
writeCache.append(inputs)
#如果是socket,则接受数据
elif each == s:
data = s.recv(BUFFER_SIZE)
print data
else:
pass
for each in writeabel:
#如果是socket并且写缓存有数据,则发送数据
if each == s and writeCache:
s.sendall(writeCache.pop())
s.close()