0

私はチャットプログラムに取り組んでいます。サーバーとクライアントがあり、複数のユーザーがサーバーに接続できます。現在、クライアントがサーバーに送信するメッセージをサーバーに送り返すだけです。認証が失敗した場合に接続を受け入れる/拒否できるように、認証を追加したいと思います。

クライアント:

class Network:
    # initialize the socket
    def __init__(self, client, host=host, port=port):
        self.client = client;
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
        self.port = port;
        self.host = host;
        self.addr = (host, port);

    # conenct to the server
    def connect(self):
        self.socket.connect(self.addr);

    # receive data from server if there is any
    def read(self):
        while True:
            time.sleep(0.1)
            try:
                data = self.socket.recv(1024);
            except:
                break;
                # instead of breaking, create "connection lost" then open the login form again
            print "in client: ", data;
            data_split = data.split("\r\n");
            for ds in data_split:
                self.client.msgbox.addMsg(ds);

    # send chat message to the server
    def send(self, msg):
        self.socket.send(msg);

    # authenticate user
    # if
    def authenticate(self, info):
        self.socket.send(info);

サーバ:

class Server:
    # init the socket
    def __init__(self, host=host, port=port):
        self.host = host;
        self.port = port;
        self.addr = (host, port);
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM);

    # send data to client
    def send(self, soc, data):
        try:
            soc.send(data);
        except:
            return "couldn't send message";

    # receive data from client
    def receive(self, soc):
        while True:
            try:
                return soc.recv(size);
            except:
                return disconnect;

    # connect client
    def connect(self):
        self.socket.bind(self.addr);
        self.socket.listen(5);
        self.socket_s = [self.socket];
        self.read_socs = [self.socket];
        self.write_socs = [];
        self.user_addr = {};

    # validate the user
    def validate(self, username, password):
        if username in users:
            sha = s256.new();
            sha.update(password);
            password = sha.hexdigest();

            if password == users[username]:
                print "in server: true";
                return True;
            else:
                print "in server: false";
                return False;

    # server
    def serve(self):
        while True:
            r_socs, w_socs, exceptions = select.select(self.read_socs, [], []);
            for s in r_socs:
                if s in self.socket_s:
                    print "accepting socket connect";
                    soc, address = s.accept();
                    print "in server: ", soc, address;
                    self.read_socs.append(soc);
                    self.write_socs.append(soc);
                    for ws in self.write_socs:
                        self.send(ws, "len(users) == " + str(len(self.write_socs)) + "\n");
                        print connection;
                else:
                    data = self.receive(s);
                    print "in server: " + data;
                    if auth in data:
                        ds = data.split(" ");
                        res = self.validate(ds[1], ds[2]);
                    elif data == disconnect:
                        s.close();
                        self.read_socs.remove(s);
                        self.write_socs.remove(s);
                        for ws in self.write_socs:
                            print "in server: " + ws
                            self.send(ws, "len(users) == " + str(len(self.write_socs)) + "\n");
                    else:
                        for ws in self.write_socs:
                            print "in server: " + ws;
                            self.send(ws, data);
4

1 に答える 1

2

受信した TCP メッセージ内のデータは、反対側からの単一の送信と必ずしも相関するとは限らないため、設計は実際には機能しません。半分のメッセージ、または 3 つのメッセージ、または 5-1/2 のメッセージである可能性があります。小さなメッセージで localhost でテストしているだけの場合、多くの場合、テストでは機能しているように見えますが、インターネットに置くと完全に失敗します。そのため、区切り文字 (改行など)、長さのプレフィックス (ネット文字列など)、または自己区切りオブジェクト (JSON など) を使用する、TCP の上にある種のプロトコルを構築する必要があります。

とにかく、各メッセージが入ってくるソケットはわかっています。ソケットをユーザーにマップするか、ソケット自体またはその fd を使用して決定を下すことができます。したがって、 に渡すすべての既知のソケットをselect追跡するのと同様に、認証済みであることがわかっているすべてのソケットも追跡します。メッセージが入ってくるソケットがそのリストにある場合、それは認証されています。それ以外の場合、認証メッセージでない限り、メッセージは拒否されます。

単純なライン プロトコルがあるとします。

def __init__(self):
    self.sockets = [] # add clients here, along with listener
    self.authsockets = [] # add authenticated clients here
    self.buffers = defaultdict(str)

def loop(self):
    r, w, x = select.select([sockets], [sockets], [sockets])
    for sock in r:
        buffers[sock] = buffers[sock] + sock.recv(4096)
        lines = buffers[sock].split('\n')
        if buffers[sock][-1] != '\n':
            buffers[sock], lines = lines[-1], lines[:-1]
        else:
            buffers[sock] = ''
        for line in lines:
            processCommand(sock, line)
    # etc.

def processCommand(self, sock, command):
    if self.isAuthCommand(command):
        if self.isValidAuthCommand(command):
            self.authsockets.append(sock)
        return
    if not sock in self.authsockets:
        return # ignore commands before auth
    self.doNormalThing(command)

受け入れ、切断、エラー、書き込みなど、無関係なものをすべて取り除きました。しかし、読み取りにも同様の問題があります。まず、ソケットは常に書き込み可能であると想定していますが、これは正しくありません。ソケットごとに書き込みバッファをキューに入れ、selectOK が表示されたら書き込む必要があります。繰り返しますが、これは localhost では機能するように見えますが、インターネットでは機能しません。第二に、ソケットへの書き込みはバッファ全体を送信しない可能性があるため、書き込まれたバイト数を確認し、次回まで buffer[bytecount:] を保持する必要があります。

于 2012-11-29T22:22:49.463 に答える