예제 #1
0
def retain_forward_link(msg_check_mem, myHashID, msgID):
    global my_tcp_conns, roomname, username, myip, myport, MsgWin, roomchat_sock, sock_peers, \
        forwardlink, backwardlink
    # get current member list
    rmsg_mem = query(msg_check_mem, roomchat_sock)
    roomhash = rmsg_mem.split(':')[1]  # rmsg_mem[0]

    retain_num = 0

    while not thread_end:
        # print('I want to retain forward link')

        rmsg_mem = query(msg_check_mem, roomchat_sock)
        # print('this is roomhash',roomhash, rmsg_mem.split(':')[1])
        if rmsg_mem.startswith("F:"):
            # this is the case that two simutaneous messages form a mal-formatted request
            continue

        # if the roomhash is changed, update the member list
        if roomhash != rmsg_mem.split(':')[1]:  # rmsg_mem[0]:
            retain_num += 1
            print("[P2P >retain_forw] retain_num: {}".format(retain_num))

            roomhash = rmsg_mem.split(':')[1]  # rmsg_mem[0]
            try:
                mems = parse_members(rmsg_mem)
            except AssertionError:
                print('[P2P >retain_forward_link][Error] pls fix here. I received: {}, I sent: {}'.format(rmsg_mem,
                                                                                                          msg_check_mem))
            # import pdb;
            # pdb.set_trace()
            mem_hashes = set(mem.HashID for mem in mems)
            print("[P2P >retain] backwardlink RIGHT BEFORE update: {}".format(backwardlink))
            backwardlink = {k: v for k, v in backwardlink.items() if k in mem_hashes}
            print("[P2P >retain] backwardlink RIGHT AFTER update: {}".format(backwardlink))

            # if the my forward server is no longer in the member list,
            # make a new forward link
            if sock_peers['forward'] not in mem_hashes:
                sock_peers, msgID, my_tcp_conns, forwardlink = forward_link(mems, myHashID, sock_peers,
                                                                            roomname, username, myip, myport, msgID,
                                                                            MsgWin,
                                                                            my_tcp_conns)
            print("[P2P >retain] forwardlink RIGHT AFTER update: {}".format(forwardlink))

        thread_event.wait(timeout=1)
예제 #2
0
def receive_and_send(rmsg, sending_sock):
    global HID_msgID_dict, roomchat_sock
    print('this is rmsg', rmsg)
    msg_split, content = parse_send_message(rmsg.decode("utf-8"))
    print(msg_split, content)
    if msg_split:  # otherwise the msg format is incorrect
        origin_roomname = msg_split[0]
        originHID = msg_split[1]
        origin_username = msg_split[2]
        msgID = msg_split[3]
        print(origin_roomname, originHID, origin_username, msgID, content)

        if origin_roomname != roomname:
            print('this is not my room')
            return
        if originHID in HID_msgID_dict and int(HID_msgID_dict[originHID]) >= int(msgID):
            err('we have recorded this message')
            return

        if not originHID in HID_msgID_dict:
            # handle the case that this is an unknown peer
            print("[Info] I don't know this guy, add it to my dict")
            msg_check_mem = 'J:{roomname}:{username}:{userIP}:{port}::\r\n'. \
                format(roomname=roomname, username=username,
                       userIP=myip, port=myport)
            try:
                newmsg = query(msg_check_mem, roomchat_sock)
                membermsg = parse_memberships(newmsg)
                memberships = membermsg[1::3]
            except:
                err('Fail to ask roomserver about the unknown sender')
            if not newmsg or newmsg[0] == 'F':
                err('Fail to ask roomserver about the unknown sender')
                return

            try:
                # if cannot find him in the group list, report an error
                idx = memberships.index(origin_username)
            except:
                err('Fail to find sender: {} in this chatroom'.format(origin_username))
                return
            print("[Info] upon asking for this unknown, I get", newmsg)

        HID_msgID_dict[originHID] = int(msgID)
        MsgWin.insert(1.0, "\n[{origin_username}] [ID: {msgID}]: {content}".format(origin_username=origin_username,
                                                                                   msgID=msgID, content=content))

        my_connections = list(backwardlink.values())
        if forwardlink:
            my_connections += [forwardlink]
        for sk in my_connections:
            if sk != sending_sock:
                sk.send(rmsg)

    else:
        print('incorrect msg format')
예제 #3
0
    def run(self):
        try:
            while not thread_end:
                show_time("[P2P >keepalive] Start waiting")

                self.waiter.wait(timeout=self.interval)
                print('[P2P >keepalive] {} greetings from {} with thread {}'.format(show_time(), self.txt, self.name))

                rmsg = query(self.msg, self.sockfd)
        finally:
            print("[P2P >keepalive] {} internally ended".format(self.name))
예제 #4
0
def do_List():
    msg = 'L::\r\n'
    rmsg = query(msg, roomchat_sock)

    groups = parse_rmsg(rmsg, prefix="G:")

    if groups == ['']:
        CmdWin.insert(1.0, "\n[List] No active chatrooms")
    else:
        for group in groups:
            CmdWin.insert(1.0, "\n    {}".format(group))
        CmdWin.insert(1.0, "\n[List] Here are the active chatrooms:")

    MsgWin.insert(1.0, "\nThe received message: {}".format(rmsg))
예제 #5
0
def check_join():
    global roomname

    if not roomname:
        return False
    if not username:
        return False
    msg_check_mem = 'J:{roomname}:{username}:{userIP}:{port}::\r\n'. \
        format(roomname=roomname, username=username,
               userIP=myip, port=myport)
    try:
        rmsg = query(msg_check_mem, roomchat_sock)
    except:
        return False
    if rmsg[0] == 'F':  # if 'F',
        roomname = Nonem
        return False
    else:
        return True
예제 #6
0
def forward_link(gList, myHashID, sock_peers_TODO,
                 roomname, username, myip, myport, msgID, MsgWin,
                 my_tcp_conns_TODO):
    '''
    This function establishes a forward link to a peer in the chatroom
    :param gList: the list of members
    :param myHashID: the hash of `myname+myip+myport`
    :param sock_peers: a dict storing my backward and forward links
    :param roomname: the name of the chatroom
    :param username: my username
    :param myip: my ip address
    :param myport: my port
    :param msgID: the ID of my message, counting from zero
    :param MsgWin:
    :param my_tcp_conns: the list of connections, which needs to be closed in do_Quit()
    :return: sock_peers, msgID, my_tcp_conns
    '''
    # print("I am in forward")
    global my_tcp_conns, forwardlink, sock_peers, multithread
    my_gList_ix = [my_gList_ix for my_gList_ix, item in enumerate(gList)
                   if item.HashID == myHashID][0]
    start = (my_gList_ix + 1) % len(gList)

    forwardlink = None

    # this while loop finds ONE peer to establish a forward link to
    while gList[start].HashID != myHashID:
        print("[loop gList] start:", start)

        # if gList[start].HashID in sock_peers['backward']:
        if gList[start].HashID in backwardlink:
            start = (start + 1) % len(gList)
        else:
            forwardlink = build_tcp_client(gList[start].ip, gList[start].port)
            # forwardlink = build_tcp_client('localhost', 32345)
            # if myport == 32342:
            #     import pdb;
            #     pdb.set_trace()

            if forwardlink != False:
                t = client_thread()
                t.daemon = True
                t.start()
                multithread += [t]
                print("[P2P >forward_link] out of client thread")
                # handshake
                msg = 'P:{roomname}:{username}:{userIP}:{port}:{msgID}::\r\n'. \
                    format(roomname=roomname, username=username,
                           userIP=myip, port=myport, msgID=msgID)
                MsgWin.insert(1.0, "\n[JOIN] peer-to-peer handshake sent msg: {}".format(msg))
                rmsg = query(msg, forwardlink)
                if rmsg.startswith('S:'):
                    sock_peers['forward'] = gList[start].HashID
                    gList[start].backward += [myHashID]
                    gList[my_gList_ix].forward = gList[start].HashID

                    msgID += 1
                    my_tcp_conns += [forwardlink]
                    # backwardlink += [forwardlink]
                    print("[P2P >forward_link] sock_peers['forward']:", gList[start].name)
                    break
                elif rmsg.startswith('F:not_member_msg::\r\n'):
                    forwardlink.close()
                else:
                    start = (start + 1) % len(gList)
            else:
                start = (start + 1) % len(gList)
    return sock_peers, msgID, my_tcp_conns, forwardlink
예제 #7
0
def build_tcp_server(msg_check_mem, waiter):
    global myip, myport, roomchat_sock, backwardlink, MsgWin, CmdWin, HID_msgID_dict
    # Step 1. create socket and bind
    sockfd = socket.socket()
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    try:
        sockfd.bind((myip, myport))
        udp.bind((myip, myport))
    except socket.error as emsg:
        print("[build_tcp_server] Socket bind error: ", emsg)
        sys.exit(1)

    print("[build_tcp_server] SERVER established at {}:{}".format(myip, myport))

    # Step 2. listen
    sockfd.listen(5)

    # add the listening socket to the READ socket list

    while not thread_end:
        RList = [sockfd, udp]
        # create an empty WRITE socket list
        WList = []
        for bwl in list(backwardlink.values()):
            RList += [bwl]

        # use select to wait for any incoming connection requests or
        # incoming messages or 10 seconds
        try:
            # Rready, Wready, Eready = select.select(RList, [], [], 10)
            Rready, Wready, Eready = select.select(RList, [], [], 1)
        except select.error as emsg:
            print("At select, caught an exception:", emsg)
            sys.exit(1)
        except KeyboardInterrupt:
            print("At select, caught the KeyboardInterrupt")
            sys.exit(1)

        # if has incoming activities
        if Rready:
            # print('I am very busy', Rready, RList, backwardlink)
            # for each socket in the READ ready list
            for sd in Rready:
                if sd == udp:
                    print('I am in udp')
                    msg, addr = sd.recvfrom(1024)
                    print("we receiver the msg", msg, flush=False)

                    if not msg:
                        print("[Error] chat server broken")
                    else:
                        print(msg, addr, flush=False)
                        rmsg = parse_rmsg(msg.decode("utf-8"), prefix="K:", suffix="::\r\n")
                        MsgWin.insert(1.0, "\n~~~~~~~~~~~~~{}~~~~~~~~~~~~~~".format(rmsg[1]))

                        print("You are poked by {}!!".format(rmsg[1]))
                        sd.sendto(str.encode("A::\r\n"), addr)
                elif sd == sockfd:
                    print('I am in tcp establishing')

                    # Step 3. accept new connection
                    try:
                        conn, addr = sd.accept()
                    except socket.error as emsg:
                        print("[build_tcp_server] Socket accept error: ", emsg)
                        break

                    # print out peer socket address information
                    print("[build_tcp_server] Connection established. Remote client info:", addr)

                    # Step 4. upon new connection, receive message
                    try:
                        rmsg = conn.recv(100).decode("utf-8")
                        print("[build_tcp_server] tried rmsg: {}".format(rmsg))
                    except socket.error as emsg:
                        print("Socket recv error: ", emsg)
                        break

                    # Step 5. enable peer-to-peer handshake
                    if rmsg.startswith('P:'):
                        print("[build_tcp_server] received: {}".format(rmsg))

                        rmsg = parse_rmsg(rmsg, prefix="P:", suffix="::\r\n")
                        msgID = rmsg[-1]

                        # if the msg is not from a peer in the same chatroom
                        # refuse it by replying 'F:not_member_msg::\r\n'
                        # else, reply with 'S:{msgID}::\r\n'
                        client_hash = sdbm_hash("{}{}{}".format(*rmsg[1:]))
                        rmsg_mems = query(msg_check_mem, roomchat_sock)
                        gList = parse_members(rmsg_mems)
                        mem_hashes = set(mem.HashID for mem in gList)
                        HID_msgID_dict[str(client_hash)] = msgID

                        if not client_hash in mem_hashes:
                            print("[Error] Detected non-member connection. Closing.")
                            msg = 'F:not_member_msg::\r\n'.format(msgID=msgID)
                            conn.send(str.encode(msg))
                        # break

                        msg = 'S:{msgID}::\r\n'.format(msgID=msgID)
                        conn.send(str.encode(msg))
                        print("[build_tcp_server] Connection established with {}:{}".format(*rmsg[2:4]))

                        # Step 6. add the backward link to connections
                        backwardlink[client_hash] = conn
                        print("[P2P >build_tcp_server] backward link first established: {}".format(backwardlink))
                        # RList += [conn]
                    else:
                        break
                else:  # it could be forward or a backward link, I don't care
                    # if sd == forward:
                    # 	print('wow this is forward')
                    # else:
                    # 	print('this is backward :(')
                    if sd in list(backwardlink.values()):
                        # print('I am in tcp chatting', sd)
                        rmsg = sd.recv(1000)
                        if rmsg:
                            receive_and_send(rmsg, sd)
예제 #8
0
def do_Poke():
    global my_udp_socket
    if not username:
        CmdWin.insert(1.0, "\n[Error] You must have a username first.")
        return

    if not check_join():
        CmdWin.insert(1.0, "\n[Error] You must join a chatroom first.")
        return

    targetname = parse_name(userentry)

    # if empty, provide list; if not empty check if it's inside the list
    msg = 'J:{roomname}:{username}:{userIP}:{port}::\r\n'.format(roomname=roomname, username=username,
                                                                 userIP=myip, port=myport)
    rmsg = query(msg, roomchat_sock)
    membermsg = parse_memberships(rmsg)
    memberships = membermsg[1::3]

    if not targetname:
        for member in memberships:
            if member != username:
                CmdWin.insert(1.0, "\n    {}".format(member))
        CmdWin.insert(1.0, "\n[Poke] To whom you want to send the poke?")
    elif targetname == username:
        CmdWin.insert(1.0, "\n[Error] Cannot poke yourselves")
    elif not targetname in memberships:
        CmdWin.insert(1.0, "\n[Error] The username you provided is not in this chatroom")
    else:
        # global mysock
        # if not mysock:
        #     print("rediculous!")\
        rmsg = query(msg, roomchat_sock)
        membermsg = parse_memberships(rmsg)
        memberships = membermsg[1::3]

        try:
            # if I use membermsg to find, and if someone use a port as username, it could cause misunderstanding
            # therefore I use membership to search among usernames instead, and recover to membermsg
            idx = memberships.index(targetname) * 3 + 1
        except:
            for member in memberships:
                if member != username:
                    CmdWin.insert(1.0, "\n    {}".format(member))
            CmdWin.insert(1.0, "\n[Poke] That member has left the chat room. To whom you want to send the poke?")
            return

        if not my_udp_socket:
            my_udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        msg = 'K:{roomname}:{username}::\r\n'.format(roomname=roomname, username=username)
        MsgWin.insert(1.0, "\n The message you are sending is " + msg)

        print("index of ", targetname, " is ", str.encode(msg),
              (membermsg[idx + 1], int(membermsg[idx + 2])), flush=False)

        my_udp_socket.sendto(str.encode(msg), (membermsg[idx + 1], int(membermsg[idx + 2])))
        my_udp_socket.settimeout(2)

        try:
            rmsg = my_udp_socket.recvfrom(1000)  # .decode("utf-8")
        except socket.timeout:
            CmdWin.insert(1.0, "\n[Error] Your [Poke] was not sent successfully")
            return
        if not rmsg:
            print("failure", flush=False)
            CmdWin.insert(1.0, "\n[Error] Poke failure")
        elif "A::\r\n" == rmsg[0].decode("utf-8"):
            print("success", flush=False)
            CmdWin.insert(1.0, "\n[Poke] You poked {}".format(targetname))
        else:
            CmdWin.insert(1.0, "\n[Error] Poke failure")
예제 #9
0
def do_Join():
    global roomname, roomchat_ip, \
        multiproc, msgID, sock_peers, \
        my_tcp_conns, forwardlink, multithread
    name = parse_name(userentry)

    if not username:
        CmdWin.insert(1.0, "\n[Error] Username cannot be empty. Pls input username and press [User].")
        return

    if not name:
        CmdWin.insert(1.0, "\n[Error] roomname cannot be empty.")
    elif check_join():
        if name == roomname:
            CmdWin.insert(1.0, "\n[Warn] You are already in room {}.".format(roomname))
        else:
            CmdWin.insert(1.0, "\n[Error] Already in a room. Cannot change rooms.")
    else:
        roomname = name
        # Step 1. join the chatroom, by sending msg to chatroom app
        msg_check_mem = 'J:{roomname}:{username}:{userIP}:{port}::\r\n'. \
            format(roomname=roomname, username=username,
                   userIP=myip, port=myport)
        rmsg = query(msg_check_mem, roomchat_sock)
        handle_join_rmsg(rmsg, roomname, CmdWin, MsgWin)
        print("[P2P >do_Join] JOIN message: {}, for input: {}".format(rmsg, msg_check_mem))

        # Step 2. keepalive, by sending JOIN msg to the chatroom app every 20 sec

        # TODO: change to thread
        # p = Process(target=keepalive, args=(msg_check_mem, roomchat_sock, username,))
        # p.start()
        # multiproc += [p]
        # print('[Info] out of keepalive')

        t = keepalive_thread(msg_check_mem, roomchat_sock,
                             username)  # ing.Thread(target=tmp_test, args=(1,)) #target=keepalive, args=(msg_check_mem, roomchat_sock, username,))
        t.daemon = True
        t.start()
        multithread += [t]

        # except:
        #     print('Unable to use multi threading on keepalive')
        #     exit(0)
        # print('[Info] out of keepalive')

        myHashID = sdbm_hash("{}{}{}".format(username, myip, myport))

        # Step 4. start my TCP server, as the server for other users to CONNECT to in the chatroom
        # TODO: chagne to thread
        # p = Process(target=build_tcp_server,
        # 		args=(msg_check_mem,))
        # p.start()
        # multiproc += [p]
        t = server_thread(msg_check_mem)
        t.daemon = True
        t.start()
        multithread += [t]
        # print('[Info] out of server thread')

        # Step 5. start a TCP client, to CONNECT another user's TCP server in the chatroom
        print('[Info] Entering forward link establishment')
        gList = parse_members(rmsg)
        sock_peers, get, my_tcp_conns, forwardlink = \
            forward_link(gList, myHashID, sock_peers, roomname,
                         username, myip, myport, msgID, MsgWin, my_tcp_conns)

        # update my TCP client when a relevant user terminates
        # (when the user that you CONNECT to terminates, you need to connect to another TCP server instead)
        # TODO: for the following process, I need to reuse `msgID` and `my_tcp_conns`
        # TODO: change to thread
        # p = Process(target=retain_forward_link,
        #             args=(msg_check_mem, myHashID, msgID))
        # p.start()
        # multiproc += [p]
        t = forwardlink_thread(msg_check_mem, myHashID, msgID)
        t.daemon = True
        t.start()
        multithread += [t]