Beispiel #1
0
    def Listen(self):
        print("server:开始监听")
        self.__server_socket.listen(128)
        self.__server_is_listening = True

        while self.__server_is_listening:
            try:
                client_socket, client_addr = self.__server_socket.accept(
                )  # 设置setblocking(False)后, accept不再阻塞
                print("连接成功,客户端ip:{},port:{}".format(client_addr[0],
                                                     client_addr[1]))

                # 一旦连接成功,开一个子线程进行通信
                client_socket.setblocking(False)  # 子线程是非阻塞模式的(需要循环判断监听线程退出)
                client_socket.settimeout(5)  # 超时值设为5s
                self.__running_client_cnt += 1
                self.__thread_cnt += 1
                self.new_client_signal.emit(
                    self.__running_client_cnt)  # 向ui发信号,更新ui
                client_name = "client{}".format(self.__thread_cnt)  # 创建子线程
                client_thread = MyThread(client_name, self.SubClientThread,
                                         [client_socket, client_name])
                client_thread.setDaemon(True)  # 子线程配置为守护线程,主线程结束时强制结束
                client_thread.start()  # 子线程启动

            except BlockingIOError:
                pass
Beispiel #2
0
    def Confirm(self):
        if self.portNum.text() == '':
            self.statusbar.showMessage("配置错误,port禁止为空")
            return
        if not IsIPV4(self.IPNum.text()):
            self.statusbar.showMessage("配置错误,非法的ip地址")
            return

        port = int(self.portNum.text())
        ip = self.IPNum.text()

        if self.__TransPorter.IsServer():
            self.__port = port
            self.__TransPorter.GetMainUI().uploadButton.setEnabled(False)
            self.__TransPorter.GetMainUI().downloadButton.setEnabled(False)
            server = self.__TransPorter.GetServer()
            server.ServerStart(port)  # 启动服务器(开始监听)
            self.statusbar.showMessage("正在监听port:{},client连接:0".format(port))
            self.__TransPorter.GetMainUI().statusbar.showMessage(
                "正在监听port:{},client连接:0".format(port))
        else:
            self.__TransPorter.GetMainUI().uploadButton.setEnabled(True)
            self.__TransPorter.GetMainUI().downloadButton.setEnabled(True)
            client = self.__TransPorter.GetClient()
            client.SetDestServer(port, ip)  # 设置目标port和ip
            client.ClientStart()  # 启动客户端
            self.statusbar.showMessage("connecting server")
            self.__TransPorter.GetMainUI().statusbar.showMessage(
                "connecting server")

            # 用一个子线程连接服务器,若连接成功,就创建心跳线程,然后结束
            connection_thread = MyThread(
                'connecting',
                self.__TransPorter.GetClient().Connect2Server)
            connection_thread.setDaemon(True)  # 子线程配置为守护线程,主线程结束时强制结束
            connection_thread.start()

        self.__TransPorter.SetWroking(True)  # 设置工作标志
        self.ShowWindow()  # 刷新窗口UI
Beispiel #3
0
    def AddConnection(self,proc,name,pabr=None):       
        # 创建一个socket连接
        new_socket = self.CreatSocket()
        if new_socket == None:
            return
        new_socket.setblocking(False)                    # 子线程是非阻塞模式的(需要循环判断监听线程退出)
        new_socket.settimeout(5)                         # 超时值设为5s                    

        # 创建一个连接子线程
        if pabr == None:    # 不关联下载进度页面,这是心跳线程
            connection_thread = MyThread(name,proc,[new_socket,name])
        else:               # 下载线程,关联下载进度页面
            connection_thread = MyThread(name,proc,[new_socket,name,pabr])

        # 子线程统一加入__sub_thread管理
        if not name in self.__sub_thread:
            self.__sub_thread[name] = [Frame(),connection_thread,new_socket]
        
        # 注册socket
        self.DataSend(new_socket,name,self.__ID.encode('utf-8')) 

        # 启动连接子线程
        connection_thread.setDaemon(True)   # 子线程配置为守护线程,主线程结束时强制结束
        connection_thread.start()           
Beispiel #4
0
class Server(QtCore.QObject):
    new_client_signal = QtCore.pyqtSignal(int)  #每连接一个client,就向ui发一个信号,更新状态栏

    # 构造函数
    def __init__(self):
        super().__init__()
        self.SaveSelfIP()  # 提取缓存本地ip
        self.__running_client_cnt = 0  # 当前连接的客户端数量
        self.__thread_cnt = 0  # 线程计数
        self.__sub_thread = dict(
        )  # 每个client线程有一个Frame对象空间,用字典管理。元素构成 (线程名:[Frame对象,thread对象,Lock对象,所属客户端ID])
        self.__sub_thread_union = dict(
        )  # 每一个客户端会和服务器建立多条连接(主连接用来更新UI、心跳连接用来检测断连、数据连接用来传输数据)
        # 用一个字典管理同一个client的所有连接。元素构成 (所属客户端ID:[心跳线程名,主线程名,文件线程名...])
        self.__sub_thread_heart = []  # 所有心跳线程集中到这。
        self.__died_client = []  # 断开的客户端ID暂存在这里,当其所有相关线程关闭后清除此项

    # 缓存本机ip地址 (构造一个UDP包但不发送 ,从中提取)
    def SaveSelfIP(self):
        self.__ip = CheckIp()

    # 获取本机IP
    def GetIP(self):
        return self.__ip

    # 启动服务器(创建监听线程和套接字)
    def ServerStart(self, port):
        # 创建一个监听套接字
        print("server:创建了监听套接字")
        self.__server_socket = socket.socket(
            socket.AF_INET, socket.SOCK_STREAM)  # 创建用于监听的socket
        self.__server_socket.setblocking(False)  # 配置为非阻塞的socket
        self.__server_is_listening = False

        # 设置并绑定监听的端口
        print("server:设置并绑定port:{}".format(port))
        self.__server_socket.bind(("", port))
        self.__port = port

        # 启动监听线程
        self.__sub_thread.clear()  # 清空子线程记录
        self.__listenThread = MyThread("监听线程", self.Listen)
        self.__listenThread.setDaemon(True)  #设置为守护线程,程序结束时强行结束监听
        self.__listenThread.start()

    # 关闭服务器(关闭所有子线程和套接字)
    def ServerShutDown(self):
        print("server:停止监听")
        self.__server_is_listening = False  # 通过共享内存和子线程线程通信,关闭所有子线程和客户端线程
        self.__listenThread.join()  # 等待监听线程和所有客户端线程结束
        print("server:监听套接字关闭")
        self.__server_socket.close()
        self.__running_client_cnt = 0

# 用于数据收发的基础方法---------------------------------------------------------------------------
# socket接受

    def BytesRecv(self, client_socket, client_name, max_size):
        data = None
        timeout = 0
        ID = self.__sub_thread[client_name][2]  # 此线程所属客户端ID
        while data == None and self.__server_is_listening and not ID in self.__died_client:
            try:
                data = client_socket.recv(max_size)
            except BlockingIOError:  # 非阻塞socket,pass此异常以实现轮询
                pass
            except ConnectionAbortedError:
                if client_name in self.__sub_thread_heart:  # 客户端断开,可能出这个异常
                    self.HeartStop(client_name)
                return '连接断开'
            except ConnectionResetError:  # 客户端断开,可能出这个异常
                if client_name in self.__sub_thread_heart:
                    self.HeartStop(client_name)
                return '连接断开'
            except socket.timeout:
                if client_name in self.__sub_thread_heart:  # 只对心跳线程做超时判断
                    timeout += 5
                    print(client_name, '连接超时', timeout)
                    if timeout == 10:
                        self.HeartStop(client_name)
                        return '连接断开'
        if not self.__server_is_listening or data == b'':  # 客户端断开,data返回空串
            return '连接断开'
        return data

    # 线性安全的socket发送
    def BytesSend(self, client_socket, data):
        client_socket.send(data)

    # 准备好接收(获取分片数)
    def GetReady2Recv(self, client_socket, client_name):
        frame = self.__sub_thread[client_name][0]

        # 接受本次通信分片数
        data = self.BytesRecv(client_socket, client_name, 256)
        if data == '连接断开':
            return '连接断开'

        frame_num = frame.DecodeFrameNum(client_name, data)
        if frame_num == -1:
            return 'crc error'

        print(client_name, '本次接收分片数', frame_num)

        # 返回分片数应答
        frame.Reset()
        ack_data = '分片{}'.format(frame_num).encode('utf-8')
        frame.Code(ack_data)
        client_socket.send(ack_data)

        return frame_num

    def GetReady2Send(self, client_socket, client_name, size, isDownload):
        frame = self.__sub_thread[client_name][0]

        # 发送本次通信分片数
        max_size = frame.GetLoadNum()  # 最大负载长字节
        n = math.ceil(size / max_size)

        print(client_name, '本次发送分片数', n)
        frame.Reset()
        if isDownload:
            n_data = frame.Code(n.to_bytes(length=8, byteorder="big"))
        else:
            n_data = frame.Code(n.to_bytes(length=1, byteorder="big"))

        self.BytesSend(client_socket, n_data)

        # 确定客户端已经装备好接受
        ack_n = self.BytesRecv(client_socket, client_name,
                               1024).decode('utf-8')
        print(client_name, 'ack:', ack_n)
        if ack_n == '连接断开':
            return '连接断开'
        if ack_n != '分片{}'.format(n):
            return '分片通信错误'
        print(client_name, "已准备好发送")
        return 'OK'

    # 接受数据
    def DataRecv(self, client_socket, client_name):
        frame = self.__sub_thread[client_name][0]

        # 先准备好接受(接受分片数)
        frame_num = self.GetReady2Recv(client_socket, client_name)
        if not type(frame_num) == int:
            return frame_num

        print(client_name, "准备接收分片", frame_num)

        # 开始接受并整合所有数据
        i = 0
        data = b''
        while i < frame_num:
            # 限制每次收帧长,可以解决部分粘包问题
            data_f = self.BytesRecv(client_socket, client_name, 256)
            data, n, errCnt = frame.Decode(client_name, data_f, data)
            if errCnt != 0:
                print(client_name, '数据接收结束,校验错误')
                return 'crc error'
            i += n

        #print(client_name,"有效数据 ",data)
        print(client_name, '数据接收结束,成功')
        return data

    # 发送数据
    def DataSend(self, client_socket, client_name, data):
        frame = self.__sub_thread[client_name][0]

        # 准备好发送
        res = self.GetReady2Send(client_socket, client_name, len(data), False)
        if res != 'OK':
            return res

        # 发送所有分片
        max_size = frame.GetLoadNum()  # 最大负载长字节
        frame.Reset()
        while len(data) > max_size:
            sub_data = data[0:max_size]
            data = data[max_size:]
            file_content = frame.Code(sub_data)  # 拼数据帧
            self.BytesSend(client_socket, file_content)  # 发送数据

        # 发送最后一个分片
        file_content = frame.Code(data)  # 拼数据帧
        self.BytesSend(client_socket, file_content)  # 发送数据

        print(client_name, "数据发送完成--------------------\n")
        return '发送完成'

# 利用基本收发方法封装的一些方法------------------------------------------------------------------
# 发送path路径下的文件和目录信息

    def SendListDir(self, client_socket, client_name, path):
        print(client_name, "请求目录", path)
        try:
            items = os.listdir(path)  # 获取路径下所有文件名
        except PermissionError:
            print("无访问权限")
            self.DataSend(client_socket, client_name, '无访问权限'.encode('utf-8'))
            return

        # 分离文件和文件夹
        floders = []
        files = []
        sizes = []
        for item in items:
            file_path = os.path.join(path, item)
            if os.path.isdir(file_path):
                floders.append(item)
            else:
                size = str(round(os.stat(file_path).st_size / 1024))
                files.append(item)
                sizes.append(size)

        floder_str = '/'.join(floders)
        files_str = '/'.join(files)
        size_str = '/'.join(sizes)
        list_str = floder_str + '<>' + files_str + '<>' + size_str

        self.DataSend(client_socket, client_name, list_str.encode('utf-8'))

    def DownloadFile(self, client_socket, client_name, file_path):
        # 计算分片数
        frame = self.__sub_thread[client_name][0]
        max_size = frame.GetLoadNum()  # 最大负载长字节
        size = os.stat(file_path).st_size
        frame_num = math.ceil(size / max_size)

        # 准备好发送数据
        res = self.GetReady2Send(client_socket, client_name, size, True)
        if res != 'OK':
            return res

        # 发送文件数据
        print(client_name, '开始发送文件')
        with open(file_path, "rb") as f:  # 直接以二进制读入,传输时不用encode和decode了
            frame.Reset()
            for i in range(frame_num):
                data = f.read(max_size)  # 最多读最大负载长字节
                file_content = frame.Code(data)  # 获取帧的字节流数据
                print(client_name, len(file_content))
                client_socket.send(file_content)

        # 关闭传输socket
        print(client_name, '文件发送完毕')
        client_socket.close()

    def UploadFile(self, client_socket, client_name):
        frame = self.__sub_thread[client_name][0]
        frame.Reset()

        # 接收文件名
        file_name = self.DataRecv(client_socket, client_name)
        if not type(file_name) == bytes:
            return file_name
        file_name = file_name.decode('utf-8')
        print(client_name, '上传请求', file_name)

        # 接收并应答分片数
        frame_num = self.GetReady2Recv(client_socket, client_name)  # 分片数
        if not type(frame_num) == int:
            return frame_num

        # 接受文件数据
        i = 0
        errCnt = 0
        desktop_path = GetDesktopPath()
        with open(desktop_path + "\\[upload]" + file_name, "wb") as f:
            while i < frame_num:
                # 限制每次收帧长,可以自动进行粘包处理
                data_f = self.BytesRecv(client_socket, client_name, 256)
                if data_f == '连接断开'.encode('utf-8'):
                    return

                data, n, err = frame.Decode(client_name, data_f)
                errCnt += err
                f.write(data)
                i += n

        # 关闭传输socket
        print(client_name, '错误帧数:', errCnt)
        client_socket.close()

# 接受数据后的处理--------------------------------------------------------------------------------
# 客户端子线程(非阻塞socket):接收客户端的各种请求

    def SubClientThread(self, client_socket, client_name):
        print(client_name + ":线程启动")

        # 给此线程一个Frame对象,用来构成帧
        if not client_name in self.__sub_thread:
            self.__sub_thread[client_name] = [
                Frame(), threading.currentThread(), ''
            ]  #字典可自动添加

        # 轮询处理客户端的命令
        while self.__server_is_listening:
            # 先检查此线程对应的客户端是不是已经断开连接,如果断开了,关闭连接
            thread_id = self.__sub_thread[client_name][2]
            if thread_id in self.__died_client:
                break

            data = self.DataRecv(client_socket, client_name)
            if type(data) == bytes:
                data = data.decode('utf-8')
            print(client_name, "接收数据", data, '-----------------------------\n')

            if data == '连接断开':
                break
            # client主线程发起注册
            elif data == 'login new client':
                self.Login(client_socket, client_name)
            # client子线程注册
            elif data in self.__sub_thread_union:
                self.__sub_thread[client_name][2] = data
                if not client_name in self.__sub_thread_union[data]:
                    self.__sub_thread_union[data].append(client_name)
            # 发送桌面
            elif data == "获取桌面":
                desktop_path = GetDesktopPath()
                if self.DataSend(client_socket, client_name,
                                 desktop_path.encode('utf-8')) == '连接断开':
                    self.StopSubThread(client_socket, client_name)
                    print(client_name, "通信结束")
                    break
            # client心跳
            elif data[:5] == 'heart':
                ID = data[6:]
                if not client_name in self.__sub_thread_union[ID]:
                    self.__sub_thread_union[ID].append(client_name)
                if not client_name in self.__sub_thread_heart:
                    self.__sub_thread_heart.append(client_name)
            # 上传
            elif data == 'upload':
                self.UploadFile(client_socket, client_name)
                break
            # 判断为目录则刷新
            elif os.path.isdir(data):
                self.SendListDir(client_socket, client_name, data)
            # 判断为文件则下载
            elif os.path.isfile(data):
                self.DownloadFile(client_socket, client_name, data)
                break
            else:
                pass

        self.StopSubThread(client_socket, client_name)
        print(client_name, "通信结束")

# 线程控制相关--------------------------------------------------------------------------------
# 结束子线程

    def StopSubThread(self, client_socket, client_name):
        # 从客户端线程集中清除此线程
        thread_id = self.__sub_thread[client_name][2]
        self.__sub_thread_union[thread_id].remove(client_name)

        # 如果这是断开的客户端的线程,且此断开客户端线程集已清空,把这个客户端ID移除
        if thread_id in self.__died_client and not self.__sub_thread_union[
                thread_id]:
            del self.__sub_thread_union[thread_id]
            self.__died_client.remove(thread_id)

        # 从全体线程集中清除此线程记录
        del self.__sub_thread[client_name]

        client_socket.close()
        self.__running_client_cnt -= 1
        self.new_client_signal.emit(self.__running_client_cnt)  # 向ui发信号,更新ui

    # 心跳线程断开后的处理
    def HeartStop(self, heart_name):
        self.__sub_thread_heart.remove(heart_name)  # 从心跳列表中移除此线程
        ID = self.__sub_thread[heart_name][2]  # 获取心跳超时的客户端ID
        self.__died_client.append(ID)  # 此心跳对应的客户端ID加入死亡client列表

    # 监听线程中启动监听socket,允许被动连接
    def Listen(self):
        print("server:开始监听")
        self.__server_socket.listen(128)
        self.__server_is_listening = True

        while self.__server_is_listening:
            try:
                client_socket, client_addr = self.__server_socket.accept(
                )  # 设置setblocking(False)后, accept不再阻塞
                print("连接成功,客户端ip:{},port:{}".format(client_addr[0],
                                                     client_addr[1]))

                # 一旦连接成功,开一个子线程进行通信
                client_socket.setblocking(False)  # 子线程是非阻塞模式的(需要循环判断监听线程退出)
                client_socket.settimeout(5)  # 超时值设为5s
                self.__running_client_cnt += 1
                self.__thread_cnt += 1
                self.new_client_signal.emit(
                    self.__running_client_cnt)  # 向ui发信号,更新ui
                client_name = "client{}".format(self.__thread_cnt)  # 创建子线程
                client_thread = MyThread(client_name, self.SubClientThread,
                                         [client_socket, client_name])
                client_thread.setDaemon(True)  # 子线程配置为守护线程,主线程结束时强制结束
                client_thread.start()  # 子线程启动

            except BlockingIOError:
                pass

    # 新连接注册
    def Login(self, client_socket, client_name):
        print('注册新client')
        # 生成一个唯一的key
        key = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))
        while key in self.__sub_thread_union:
            key = ''.join(random.sample('abcdefghijklmnopqrstuvwxyz', 10))

        # 主线程加入字典
        self.__sub_thread_union[key] = [client_name]
        self.__sub_thread[client_name][2] = key

        # 返回key
        self.DataSend(client_socket, client_name, key.encode('utf-8'))