Socket---基於IO複用實現異步非阻塞通信 Python羣聊工具

運行環境:
1. 需要安裝最新的python3
sudo apt-get install python3
2. 
不要用ssh shell工具連接linux機器,因爲它唯一缺點就是不支持中文
可以選擇使用putty連接linux,編碼設置使用utf8編碼,這樣就可以輸入中文了

運行方法:
1. 將附件腳本拷貝到linux中
2. 在linux命令行下輸入:python3 client.py
3. 看到Connection established, you can input to talk now, enjoy your time!  後可以輸入說話的內容了


待改進地方:
1. UI取代命令行
2. 更多的異常處理

 

 

服務器端程序:

import socket
import signal
import select
MAX_LISTEN_N = 100
MAX_BUFFER_N = 1024
mconnections = {}
addresses = {}
datalist = {}
IP_ADDR = '192.168.1.2'
PORT = 4547

def remove_fd_data(fd):
    mconnections.pop(fd)
    addresses.pop(fd)
    datalist.pop(fd)
    
    

def signal_handler(signum, frame):
    print("signum=",signum)
    
def prepare_socket():
    #print('prepare_socket')
    listen_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)
    listen_fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,True)
    listen_fd.bind((IP_ADDR,PORT))
    listen_fd.listen(MAX_LISTEN_N)
    signal.signal(signal.SIGILL,signal_handler)
    signal.signal(signal.SIGPIPE,signal_handler)
    return listen_fd

def handle_accept(listen_fd,epoll_fd):
    client_socket,(client_addr,client_port) = listen_fd.accept()
    print('accept a new client:',client_socket.fileno())
    epoll_fd.register(client_socket.fileno(),select.EPOLLIN)
    mconnections[client_socket.fileno()]=client_socket
    addresses[client_socket.fileno()]=client_addr
    datalist[client_socket.fileno()]=''

def handle_read(fd,epoll_fd):
    print('handle_read:',fd)
    data_s=''
    while True:
        data=mconnections[fd].recv(MAX_BUFFER_N)
        #print(data)
        if not data and not data_s:
            epoll_fd.unregister(fd)
            mconnections[fd].close()
            remove_fd_data(fd)
            print("Client is closed abnormally:",fd)
            return
        elif len(data)==MAX_BUFFER_N:
            data_s+=data.decode('utf-8')
            continue
        else:
            data_s+=data.decode('utf-8')
            break
    #print("receive new data:",data_s)       
    datalist[fd]=data_s
    epoll_fd.modify(fd,select.EPOLLOUT)

def handle_write_single(epoll_fd, fd, talk):
    print("handle_write_single:", fd)
    slen = 0
    while True:
        slen+=mconnections[fd].send(talk[slen:])
        if slen == len(talk):
            #print('send finished')
            break
        else:
            print('send continue')
            break
    epoll_fd.modify(fd,select.EPOLLIN)
    
def get_host_name(fd):
    name,alias,ips = socket.gethostbyaddr(addresses[fd])
    if len(ips)>0:
        return ips[0]
    else:
        return 'Unknown name'       
        
def handle_write(fd,epoll_fd):
    #print('handle_write')
    mset = mconnections.keys()
    #length = len(mset)
    #print("length:",length)
    ip_name = get_host_name(fd)+':  '

    #handle normal exit case
    if True == (datalist[fd] == 'exit'):
        print("Client want to exit now:",fd)
        handle_write_single(epoll_fd,fd,datalist[fd].encode('utf-8'))        
        epoll_fd.unregister(fd)
        mconnections[fd].close()
        remove_fd_data(fd)
        return
    
    for item in mset:
        print(fd,":",item)
        if fd == item:
            print('ignore itself')
        else:
            handle_write_single(epoll_fd,item,(ip_name+datalist[fd]).encode('utf-8'))
    epoll_fd.modify(fd,select.EPOLLIN)
        
    
def handle_events(poll_list, listen_fd,epoll_fd):
    #print('handle_events')
    for fd, events in poll_list:
        if fd == listen_fd.fileno() and events==select.EPOLLIN:
            handle_accept(listen_fd, epoll_fd)
        elif events==select.EPOLLIN:
            handle_read(fd,epoll_fd)
        elif events==select.EPOLLOUT:
            handle_write(fd,epoll_fd)
        elif events==select.EPOLLHUP:
            print("Client is closed:",fd)
            epoll_fd.unregister(fd)
            mconnections[fd].close()
            remove_fd_data(fd)
        else:
            continue

def prepare_epoll(listen_fd):
    #print('prepare_epoll')
    epoll_fd = select.epoll()
    epoll_fd.register(listen_fd.fileno(),select.EPOLLIN)
    while True:
        poll_list = epoll_fd.poll(-1,-1)
        handle_events(poll_list, listen_fd,epoll_fd)
    epoll_fd.close()
        
    

if __name__ == '__main__':
    listen_fd=prepare_socket()
    prepare_epoll(listen_fd)
    listen_fd.close()


 

客戶端程序:

import socket
import signal
import select
import threading
MAX_BUFFER_N = 1024
IP_ADDR = '192.168.1.2'
PORT = 4547
epoll_obj = {}

def exit_client(socket_obj):
    epoll_obj[0].unregister(0)

def init_socket():
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM,0)


def prepare_socket(socket_obj):
    socket_obj.connect((IP_ADDR,PORT))
    w_msg = '''Connection established, you \
can input to talk now, enjoy your time!'''
    print(w_msg)


def handle_read(socket_obj,fd): 
    #print('handle_read')
    data_s=''
    is_exit = False
    while True:
        data=socket_obj.recv(MAX_BUFFER_N)
        #print(data)
        if not data and not data_s:
            epoll_obj[1].unregister(fd)
            print('error when read')
            is_exit = True
            return is_exit
        elif len(data)==100:
            data_s+=data.decode('utf-8')
            continue
        else:
            data_s+=data.decode('utf-8')
            break
        
    if True == ('exit' == data_s):
        epoll_obj[1].unregister(fd)
        is_exit = True
        print('I will exit now, see you later')
        return is_exit
    print('          ',data_s)
    return is_exit
    
    
def handle_write_for_0(socket_obj,fd):   
    #print('handle_write_for_0')
    is_exit = False
    data = input()
    socket_obj.send(data.encode('utf-8'))
    if True == ('exit' == data):
        is_exit = True
        epoll_obj[0].unregister(0)
    return is_exit
    
def handle_events(socket_obj,poll_list): 
    #print('handle_events')
    is_exit = False
    for fd, events in poll_list:
        #print("fd:",fd)
        if fd == 0:
            if events==select.EPOLLIN:
                continue
            elif events==select.EPOLLOUT:
                is_exit = handle_write_for_0(socket_obj,fd)
            elif events==select.EPOLLHUP:
                print("close:",fd)
                epoll_obj[0].unregister(fd)
                is_exit = True
            else:
                continue
        else:
            if events==select.EPOLLIN:
                is_exit = handle_read(socket_obj,fd)
            elif events==select.EPOLLOUT:
                continue
            elif events==select.EPOLLHUP:
                print("close:",fd)
                epoll_obj[1].unregister(fd)
                is_exit = True
            else:
                continue
    return is_exit

def start_p(socket_obj):  
    #print("start_p")
    epoll_obj[0] = select.epoll()
    epoll_obj[0].register(0,select.EPOLLOUT)   
    while True:
        poll_list = epoll_obj[0].poll(-1,-1)
        is_exit = handle_events(socket_obj,poll_list)
        if is_exit==True:
            break
    epoll_obj[0].close()
    #print("leave start_p")
    

def process_input(socket_obj):   
    #print("process_input")
    t1 = threading.Thread(group=None,target=start_p, args=(socket_obj,))
    t1.start()
    #print("leave process_input")
   
    
def prepare_epoll(socket_obj):   
    #print('prepare_epoll')
    epoll_obj[1] = select.epoll()
    epoll_obj[1].register(socket_obj.fileno(),select.EPOLLIN)   
    while True:
        poll_list = epoll_obj[1].poll(-1,-1)
        is_exit = handle_events(socket_obj,poll_list)
        if is_exit==True:
            break
    epoll_obj[1].close()
    #print("leave prepare_epoll")
         
    

if __name__ == '__main__':   
    socket_obj=init_socket()
    prepare_socket(socket_obj)
    process_input(socket_obj)
    prepare_epoll(socket_obj)
    socket_obj.close()


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章