栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

局域网内服务端与客户端通信程序

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

局域网内服务端与客户端通信程序

局域网内服务端与客户端通信程序 前言

All right, bitches and hoes. 好久不见集美们,本可儿华丽回归写文章噜,虽然只是一些赔钱的小心得,也权当是记录一下吧。这次写的是关于局域网服务端和客户端的通信程序,使用到的是python语言。

使用工具

serversocket库
pysimplegui库(使用这个库真的很方便,但是本可人翻遍了全站几乎没有找到合适的中文教程文章。有些文章看起来是翻译了,实则没有一点用处)

核心代码

服务端

class ThreadingTCPServer(socketserver.ThreadingMixIn,socketserver.TCPServer):#继承ThreadingMixIn,使其支持多线程
    pass
    
class Server(socketserver.baseRequestHandler):
    def setup(self):
        connection_pool.append(self.request)
        connection_name.append(self.client_address)
        msg = '服务端已连接,你的客户序号为{}'.format(len(connection_pool)-1) # 客户端连接上了,服务器发送的消息
        self.request.sendall(msg.encode('utf-8'))
        print_msg(server_name,'{}已连接'.format(self.client_address))

    def handle(self):
        while True:
            try:
                data = self.request.recv(1024).decode('utf-8')
                if len(data) == 0:#判断链接已经断开
                    connection_pool.remove(self.request)
                    connection_name.remove(self.client_address)
                    break
                else:
                    commute_from = connection_name.index(self.client_address)
                    print_msg('{} to {}'.format(commute_from,server_name), data)
            except:
                connection_name.remove(self.client_address)
                connection_pool.remove(self.request)

    def finish(self):
        print_msg(server_name,'{}退出连接'.format(self.client_address))

分析:这个类继承自baseRequestHandler类,是服务端用于处理socket对象通信的主类。实现处理socket通信,需要重写baseRequestHandler类中的三个方法:setup、handle和finish。类中的调用方法顺序为setup()–>handle()–>finish()。handle方法中的while True循环用于一直等待接受客户端发来的消息。
客户端

class Client:
    def __init__(self,ip,port):
        self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.s.connect((ip,port)) #ip 和 port通过界面输入

    def send(self,msg):#客户端发送数据方法
        try:
            self.s.sendall(msg.encode('utf-8'))
            self.print_msg(self.get_socket(),msg)
        except Exception as e:
            self.print_msg(self.get_socket(),'断开连接')
            self.con_close()
            
    def print_msg(self,name,msg): #在客户端界面显示信息
        print(time.ctime())
        print('{}:{}'.format(name,msg))

    def receive(self):#客户端接收信息
        while True:
            msg = self.s.recv(1024).decode(encoding='utf-8')
            if len(msg) != 0:
                self.print_msg('Server',msg)
            else:
                break

    def get_socket(self):#获取客户端的socketname
        return str(self.s.getsockname())

    def con_close(self): #关闭socket对象
        self.s.shutdown(2)
        self.s.close()

分析:客户端单使用socket库实现,需要注意的是,关闭socket连接需要在close之前调用shutdown方法,并设置参数为2,表示关闭双向连接(发送和接受)。调用了close()函数,程序中只是确保了对于某个特定的进程或线程来说,该连接是关闭的;但socket只有在所有的进程调用了close()或者socket超出了工作范围时,才会真正的被关闭或删除。在本程序中,单调用close()会出错。
客户端界面

def window1():
    layout = [[ps.Output(size=(80,40),key='output')],
              [ps.InputText(size=(40,10),key='input',do_not_clear=False),ps.Button('send')]]
    return ps.Window('ChatRoomClient',layout=layout,grab_anywhere=True)

def window2():
    Ip_layout = [[ps.Text('IP地址'), ps.InputText(size=(15, 1), key='ip',do_not_clear=True), ps.Text('端口'), ps.InputText(size=(6, 1), key='port',do_not_clear=True),
                  ps.Button('connect')]]
    return ps.Window('IpAndPort',layout=Ip_layout)
    
def main():
    ip_window = window2()
    while True:
        event,value = ip_window.read()
        if event == ps.WIN_CLOSED:
            ip_window.close()
            break
        elif event == 'connect':
            if value['ip'] == '' or value['port'] == '':
                ps.popup('警告','连接的ip地址和端口号不能为空',keep_on_top=True)
            else:
                ip = value['ip']
                port = int(value['port'])
                if re.match(r'(d|[1-9]d|1d{2}|2[0-4]d|25[0-5])(.(d|[1-9]d|1d{2}|2[0-4]d|25[0-5])){3}',ip) and re.match(r'[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]{1}|6553[0-5]',value['port']):
                    ip_window.hide()
                    msg_window = window1()#创建socket对象
                    client = Client(value['ip'], port)
                    recv_thread = threading.Thread(target=client.receive)#新开线程,循环接受信息
                    recv_thread.daemon = True #设置守护线程,主线程退出后强制终止子线程
                    recv_thread.start()
                    while True:
                        event1, value1 = msg_window.read()
                        if event1 == 'send':
                            if value1['input'] == '':
                                ps.popup('警告','发送的内容不能为空',keep_on_top=True)
                            else:
                                client.send(value1['input'])
                        elif event1 == ps.WIN_CLOSED:
                            client.con_close()
                            msg_window.close()
                            ip_window.un_hide()
                            break
                else:
                    ps.popup('警告','输入的ip地址格式或端口格式不对',keep_on_top=True)

分析:使用pysimplegui库,基本的使用方法在站里都可以找到,新开一个线程,专门用来执行阻塞模式下socket对象接收信息的方法,在主线程里直接接受信息的话会出现只有服务端发来信息,客户端才能发信息的的美丽景象,真的很美惹。
服务端界面

def window():
    layout = [[ps.Output(size=(80, 40), key='output')],
              [ps.InputText(size=(3, 1),key='client'),ps.InputText(size=(40, 10), key='input',do_not_clear=False), ps.Button('send'),ps.Button('show')]]
    return ps.Window('ChatRoomServer', layout=layout, grab_anywhere=True)


def main():
    server = ThreadingTCPServer((ip, port), Server)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    msg_window = window()
    print_msg(server_name,'等待用户连接中。。。')
    while True:
        event,value = msg_window.read()
        if event == ps.WIN_CLOSED:
            if len(connection_pool) != 0:
                for request in connection_pool:
                    request.sendall('服务器即将关闭'.encode('utf-8'))
                time.sleep(2)
                server.shutdown()
                server.server_close()
            msg_window.close()
            break
        elif event == 'send':
            if value['client'] == '' or value['input'] == '':
                ps.popup('警告','通信内容和通信对象不能为空',keep_on_top=True)
            else:
                commute_to = int(value['client'])
                if commute_to >= len(connection_pool):
                    ps.popup('警告','该序号下没有用户',keep_on_top=True)
                else:
                    connection_pool[commute_to].sendall(value['input'].encode('utf-8'))
                    print_msg('{} to {}'.format(server_name,commute_to),value['input'])
        elif event == 'show':
            if len(connection_name) != 0:
                print_msg(server_name,'当前用户列表如下')
                for index in range(len(connection_name)):
                    print('{}:{}'.format(index,connection_name[index]))
            else:
                print_msg(server_name,'当前尚无用户连接')

分析:新建一个线程并将其设置为守护线程用于整体管理服务端,主线程响应界面输入

结果展示

服务端显示两个客户接入

向0号客户发送信息

0号客户端收到

1号客户发送信息

服务端收到信息

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/679481.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号