[Project]聊天室-Python

目的:通过一个小项目练习与学习Python

服务端:

ChatServer类:
  attr:
    server:服务器Socket
    CONNECTIONS:包含所有已连接的Client Socket与Server Scoket
    host、post 主机、端口
    RCVBUF:接受缓冲区大小
  methods:
    broadcast_data:每接受一次信息,将信息发送给其它所有客户端
    run:通过select实现非阻塞监听

客户端:

Client类:
  attr:
    s:Client Socket
    host_name、ip: 本机名、本机ip
    des_host、des_port:服务器地址与端口
  methos:
    hint:刷新标准输出缓冲区
    run:监听stdin与Client Socket

Tips:

   客户端无法再Windows下使用
   "On Windows, the underlying select() function is provided by the WinSock library, and does not handle file descriptors that don’t originate from WinSock" ---Python Doucumentation

   服务端运行在远程服务器时以服务器地址做参数构造Server

GUI编程

1.通过修改text['State']='disabled'可以使文本域不可被编辑。同样当自己需要插入时需要先改变State为normal
2.如何使Scrollbar始终指向文本域底部?通过text.see(END)可实现。参见说明:
QQ截图20161205122250.png

遇到问题

  1. 点击'发送时',提示超时,发送失败。

  2. 在Ubuntu环境下测试时,发现每次连接后发送消息后会自动断开连接。
    排除客户端错误,在服务端代码对应位置插入异常输出,提示str类型异常,发现
    self.broadcast_data(set(self.CONNECTIONS)-{sock},("\r" + '<' + str(sock.getpeername()) + '> ' +data).encode('ascii'))
    data忘记做了类型转化,data改为data.decode('ascii')

  3. 回到第一个问题,发现每次提示发送超市关闭GUI时,服务端输出连接成功。原来root.mainloop()循环会阻塞run中监听循环.在点击发送时实际上并未连接至服务器.
      解决方案:将run放入另一个线程中便可解决。

  4. 使用os_exit()方法退出进程,在测试时发现服务端报错

    "\rClient {} is offline\n".format(sock.getpeername()))
    OSError: [Errno 107] Transport endpoint is not connected

    猜测os_exit()使得后面代码没有被正常执行,客户端已关闭socket,服务端调用socket方法出现错误。
    解决方法:将os._exit()去掉,使得服务端能正常关闭socket,然后通过chat.run线程退出进程。

下一步:

  1. 补全Save功能
  2. 处理非正常退出进程

更新0315

  1. 已补全Save功能
  2. 已处理非正常退出问题
  3. json打包发送数据
    代码见https://github.com/starsD/pyChatroom

Python解九连环问题

设九连环从右至左每个环编号为1,2,3,……,9
每个环只有两种状态up,down(即在环上与不在环上)

解九连环基本操作:

1.若要卸下一个环n(state[n]=up ->state[n]=down):(n>1)
必须先使state[n-1]=up,state[n-2]state[n-3]----、state[0]=down
2.若要装上一个环n(state[n]=down ->state[n]=up):(n>1)
必须先使state[n-1]=up,state[n-2]state[n-3]----、state[0]=down
3.n=1的环永远可以自由改变状态

具体思路:
假设九连环的前七个环已解,则当前九连环状态为:


 9   8   7    6    5    4   3    2    1
U  U  D  D  D  D  D  D  D


故只需卸下第九个环(一步完成),然后卸下第八个环(运用三个规则递归调用)

则总体基本流程为:

solve(n)
    solve(n-2)
    down(n)
    ddown(n-1)

具体代码如下:

    i = 0
    
    def solvejlh(n):
        if n == 1:
            ddown(1)
            return
        elif n == 2:
            ddown(2)
            ddown(1)
            return
        solvejlh(n - 2)
        ddown(n)
        down(n-1)
    
    
    def up(n):
        if n == 1:
            dup(n)
        else:
            up(n - 1)
            dup(n)
            down(n - 1)
    
    
    def down(n):
        if n == 1:
            ddown(n)
        else:
            up(n - 1)
            ddown(n)
            down(n - 1)
    
    
    def dup(n):
        global i
        print('No.{} up'.format(n))
        i += 1
    
    
    def ddown(n,):
        global i
        print('No.{} down'.format(n))
        i += 1
    
    solvejlh(9)
    print(i)

子集构造法实现NFA转DFA

    import os
    import pprint
    import argparse
    
    dic = {}
    az = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    count = 0
    DFAlist=[]
    
    #记录路径信息到DFA列表
    def recordDFA(list_1, cha, list_2):
        global DFAlist
        lis=[list_1,cha,list_2]
        DFAlist.append(lis)
    #输出DFA列表
    def outputDFA():
        global DFAlist
        global dic
        print('                     NFA状态               DFA状态   读取字符   到达DFA状态             到达NFA状态')
        for eachlist in DFAlist:
            key1=''
            key2=''
            for each in eachlist:
                for key in dic.keys():
                    if dic[key]==each and key1=='':
                        key1=key
                    elif dic[key]==each and key1!='':
                        key2=key
            print(str('%40s'%eachlist[0])+'     '+key1+'       '+str(eachlist[1])+'       '+key2+'      '+str(eachlist[2]))
    
    #类中含list与标记flag
    class listflag:
        unsignedcount = 0
    
        def __init__(self, list_0):
            self.list = list_0
            self.flag = 1
            listflag.unsignedcount += 1
    
        def getlist(self):
            return self.list
    
        def getflag(self):
            return self.flag
        #将对象设置标记
        def setflag(self, flag):
            if flag == 1:
                listflag.unsignedcount += 1
            else:
                listflag.unsignedcount -= 1
            self.flag = flag
        #返回当前是否含有未被标记的对象
        @staticmethod
        def isHaveUnsignedFlag():
            return listflag.unsignedcount > 0
    
    
    # 打开NFA文件并将内容转换为三维列表并返回
    def openNFA(path):
        list_3 = []
        if os.path.exists(path) != True:
            print('The path is wrong')
        try:
            with open(path, 'r') as file:
                for line in file.readlines():
                    line = line.rstrip('\n')  # 去掉每一行的换行符
                    tmp_list = line.split(';')  # 存在通过一条边有多个终点的节点,见目的节点放在列表中
                    for i in range(0, len(tmp_list)):
                        tmp_list[i] = tmp_list[i].split(',')
                    list_3.append(tmp_list)  # 生成三维列表
        finally:
            file.close()
        return list_3
    
    
    # 参数:起始状态(list) NFA列表 经过的边(z表示其他) 返回目标状态列表
    def moveTo(state_start_list, list_3, border='z'):
        row = len(list_3)  # 三维列表行 列 起始状态位置 边位置 返回列表
        col = len(list_3[0])
        state_row = 0
        border_col = 0
        list_end = []
        for i in range(0, col):  # 取得边的位置
            if list_3[0][i][0] == border:
                border_col = i
                break
        for state_start in state_start_list:
            state = str(state_start)
    
            for i in range(0, row):  # 取出起始状态位置
                if list_3[i][0][0] == state:
                    state_row = i
                    break
            if list_3[state_row][border_col][0] == 'void':  # 获得对应NFA表信息并处理
                pass
            else:
                for each in list_3[state_row][border_col]:  # 将能到达的状态放入返回列表中
                    list_end.append(each)
        return sorted(list_end)
    
    
    # 求一个起始状态集合的闭包
    def closureT(T_list, list_3):
        list = T_list
        states = []
        for item in T_list:
            states.append(str(item))  # 详见算法
        while len(list) != 0:
            state = list.pop()
            u = moveTo([state], list_3)
            for each in u:
                if states.count(each) == 0:
                    states.append(each)
                    list.append(each)
        return sorted(states)
    
    
    # 给出初始状态s0与NFA三维列表 输出DFA
    def DFA(state0, list_3):
        global dic
        global count
        global az
        Dstates = []
        #计算初始状态闭包
        closure0 = closureT(state0, list_3)
        #将旧状态与新状态映射放在字典中
        dic[az[count]]=closure0
        count+=1
        #详见算法
        temp = listflag(closure0)
        Dstates.append(temp)
        characters = []
    
    
        for character in list_3[0][1:len(list_3[0]) - 1]:
            characters.append(character[0])
        while listflag.isHaveUnsignedFlag():
            T = []
            for each in Dstates:
                if each.getflag() == 1:
                    T = each.getlist()
                    each.setflag(0)
                    break
            for character in characters:
                U = closureT(moveTo(T, list_3, border=character), list_3)
                flag = True
                for obj in Dstates:
                    if U == obj.getlist():
                        flag = False
                if flag:
                    Dstates.append(listflag(U))
                    dic[az[count]]=U
                    count+=1
    
                #print('%40s'%str(T)+'--->'+character+'--->'+str(U))
                #记录路由信息
                recordDFA(T,character,U)
        return Dstates
    
    def parse_args():
        parser = argparse.ArgumentParser()
        help="Example NFA.txt:(line0)\n"+ \
             "10;0;1;z(line1)\n" +\
             "A;A,B;A;void(line2)\n"+ \
             "B;C;void;void(line3)\n" +\
             "C;void;void;void(line4)\n"
        parser.add_argument('-f', help=help, default='')
        args = parser.parse_args();
        return args
    if __name__=='__main__':
        args=parse_args()
        filepath=args.f
        if os.path.exists(filepath):
            temp = openNFA(filepath)
            #输出有文件得到的NFA表
            print('NFA TABLE:')
            pprint.pprint(temp)
            #获得起始状态并开始
            startcha = temp[1][0][0]
            Dstates = DFA([startcha], temp)  # 若将temp[0]作参数,由于传入的是引用,则会在后面出栈时破坏NFA表
            outputDFA()
        else:
            print('file  not  exists!')

NFA.txt

ubuntu 命令 自用

ps -ef ->显示当前所有进程
pstree ->以树状形式显示进程

tar -czf target.tar target
tar -cf result.tar f1 f2
kill -9 pid ->结束进程
kill -stop pid ->暂停进程

jobs ->查看后台进程
fg id ->将进程放到前台
bg id ->将进程放到后台

screen -S name ->新建一个名为name的screen
screen -d name/pid ->与窗口断开连接
screen -r name/pid ->重连窗口
screen -S -X id quit -> 关闭窗口
exit ->结束窗口

df -h ->查看硬盘使用空间
du -hs name->查看目录所占空间
du -s name ->查看文件夹大小
rm -rf name ->删除文件夹

free ->查看内存使用情况

sftp -oPort=2730 root@104.225.153.5 -> SFTP连接服务器
put [-r] [localfile] [serverfile] -> 上传文件
get [-r] [serverfile] [localfile] -> 下载文件
!ls -> 列出本地文件