1. 程式人生 > >[Python 網絡編程] TCP Client (四)

[Python 網絡編程] TCP Client (四)

utf8 config readn ogg enum target 一個 net t對象

TCP Client

  • 客戶端編程步驟:
  • 創建socket對象
  • 連接到服務端的ip和port,connect()方法
  • 傳輸數據
    •   使用send、recv方法發送、接收數據
  • 關閉連接,釋放資源

最簡單的客戶端:

#TCP Client客戶端
import socket

client = socket.socket()
client.connect((‘127.0.0.1‘,9999))

client.send("Hi, I‘m client1.".encode())
client.close()

#運行

  

服務端狀態:

[16:08:25]	 [showthreads,1796] [<_MainThread(MainThread, started 9816)>, <Thread(show_client, started daemon 9344)>, <Thread(accept, started daemon 5908)>, <Thread(showthreads, started 1796)>]
[16:08:26]	 [accept,5908] <socket.socket fd=424, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 9999), raddr=(‘127.0.0.1‘, 5287)>-(‘127.0.0.1‘, 5287)
[16:08:26]	 [show_client,9344] {(‘127.0.0.1‘, 5287): <_io.TextIOWrapper mode=‘rw‘ encoding=‘utf8‘>}
2017/12/24 16:08:26 127.0.0.1:5287
Hi, I‘m client1.

[16:08:26]	 [recv,980] 2017/12/24 16:08:26 127.0.0.1:5287
Hi, I‘m client1.

[16:08:26]	 [recv,980] (‘127.0.0.1‘, 5287) quit
[16:08:28]	 [showthreads,1796] [<_MainThread(MainThread, started 9816)>, <Thread(show_client, started daemon 9344)>, <Thread(accept, started daemon 5908)>, <Thread(showthreads, started 1796)>]

  

將上面的TCP Client封裝成類:

1)搭架子

#TCP Client客戶端 封裝成類
import socket


class ChatClient:
    def __init__(self):
        pass

    def start(self):
        pass

    def _recv(self):
        pass

    def send(self):
        pass

    def stop(self):
        pass

  

2)基礎功能

客戶端:

#TCP Client客戶端 封裝成類
import socket,threading,logging,datetime
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)


class ChatClient:
    def __init__(self,ip=‘127.0.0.1‘,port=9999):
        self.sock = socket.socket()
        self.addr = (ip,port)

        self.event = threading.Event()
        self.start()


    def start(self):
        self.sock.connect(self.addr)

        # 準備接收數據,recv是阻塞的,啟動新的線程
        threading.Thread(target=self._recv,name=‘recv‘).start()


    def _recv(self):
        while not self.event.is_set():
            try:
                data = self.sock.recv(1024) #阻塞

            except Exception as e:
                logging.info(e) #有任何異常保證退出
                break
            msg = "{:%H:%M:%S} {}:{}\n{}\n".format(datetime.datetime.now(),*self.addr,data.decode().strip())
            # print(type(msg),msg)
            logging.info("{}".format(data.decode()))

    def send(self,msg:str):
        data = "{}\n".format(msg.strip()).encode()
        self.sock.send(data)

    def stop(self):
        logging.info("{} broken".format(self.addr))
        self.sock.close()

        self.event.wait(3)
        self.event.set()
        logging.info("byebye")


def main():
    e = threading.Event()
    cc = ChatClient()

    while True:
        msg = input(">>> ")
        if msg.strip() == ‘quit‘:
            cc.stop()
            break
        cc.send(msg)


if __name__ == ‘__main__‘:
    main()

  

服務端:

#TCP Server 改裝成makefile
import threading,logging,time,random,datetime,socket
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]\t [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)

class ChatServer:
    def __init__(self,ip=‘127.0.0.1‘,port=9999): #啟動服務
        self.addr = (ip,port)
        self.sock = socket.socket()
        self.event = threading.Event()

        self.clients = {} #客戶端

    def show_client(self):
        while not self.event.is_set():
            if len(self.clients) > 0:
                logging.info(self.clients)
                self.event.wait(3)


    def start(self):
        self.sock.bind(self.addr)
        self.sock.listen()
        # accept會阻塞主線程,所以開一個新線程
        threading.Thread(target=self._accept,name=‘accept‘,daemon=True).start()
        threading.Thread(target=self.show_client,name=‘show_client‘,daemon=True).start()


    def stop(self):
        for c in self.clients.values():
            c.close()
        self.sock.close()
        self.event.wait(3)
        self.event.set()

    def _accept(self):
        while not self.event.is_set(): #多人連接
            conn,client = self.sock.accept()  #阻塞
            f = conn.makefile(mode=‘rw‘)
            self.clients[client] = f

            logging.info("{}-{}".format(conn,client))
            # recv 默認阻塞,每一個連接單獨起一個recv線程準備接收數據
            threading.Thread(target=self._recv, args=(f, client), name=‘recv‘,daemon=True).start()

    def _recv(self, f, client): #接收客戶端數據
        while not self.event.is_set():
            try:
                data = f.readline()
            except Exception:
                data = ‘quit‘
            finally:
                msg = data.strip()
                # Client通知退出機制
                if msg == ‘quit‘:
                    f.close()
                    self.clients.pop(client)

                    logging.info(‘{} quit‘.format(client))
                    break

            msg = "{:%Y/%m/%d %H:%M:%S} {}:{}\n{}\n".format(datetime.datetime.now(),*client,data)
            # msg = data
            print(msg)
            logging.info(msg)

            for c in self.clients.values():
                # print(type(msg))
                c.writelines(msg)
                c.flush()



cs = ChatServer()
print(‘!!!!!!!!!!!‘)
cs.start()
print(‘~~~~~~~~~~~~~~~~~~~~‘)
e = threading.Event()
def showthreads(e:threading.Event):
    while not e.wait(3):
        logging.info(threading.enumerate())

threading.Thread(target=showthreads,name=‘showthreads‘,args=(e,)).start()

while not e.wait(1): # Sever控制臺退出方式
    cmd = input(‘>>> ‘).strip()
    if cmd == ‘quit‘:
        cs.stop()
        e.wait(3)
        break

  

運行結果:

#服務端
~~~~~~~~~~~~~~~~~~~~
>>> [17:26:14]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:14]	 [accept,3832] <socket.socket fd=400, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 9999), raddr=(‘127.0.0.1‘, 7517)>-(‘127.0.0.1‘, 7517)
[17:26:15]	 [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:17]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:18]	 [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:19]	 [recv,2112] 2017/12/24 17:26:19 127.0.0.1:7517
hello1


2017/12/24 17:26:19 127.0.0.1:7517
hello1


[17:26:20]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:21]	 [showthreads,5928] [<Thread(accept, started daemon 3832)>, <Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(showthreads, started 5928)>]
[17:26:23]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:23]	 [accept,3832] <socket.socket fd=436, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘127.0.0.1‘, 9999), raddr=(‘127.0.0.1‘, 7539)>-(‘127.0.0.1‘, 7539)
[17:26:24]	 [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
2017/12/24 17:26:25 127.0.0.1:7539
[17:26:25]	 [recv,6748] 2017/12/24 17:26:25 127.0.0.1:7539
hello2
hello2




[17:26:26]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>, (‘127.0.0.1‘, 7539): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:27]	 [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:29]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>, (‘127.0.0.1‘, 7539): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:30]	 [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:32]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>, (‘127.0.0.1‘, 7539): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:33]	 [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]
[17:26:35]	 [show_client,7824] {(‘127.0.0.1‘, 7517): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>, (‘127.0.0.1‘, 7539): <_io.TextIOWrapper mode=‘rw‘ encoding=‘cp936‘>}
[17:26:36]	 [showthreads,5928] [<Thread(show_client, started daemon 7824)>, <Thread(recv, started daemon 2112)>, <_MainThread(MainThread, started 7412)>, <Thread(accept, started daemon 3832)>, <Thread(recv, started daemon 6748)>, <Thread(showthreads, started 5928)>]

  

#客戶端1
>>> hello1
[17:26:19]	 [recv,2604] 2017/12/24 17:26:19 127.0.0.1:7517
hello1
>>> 

[17:26:25]	 [recv,2604] 2017/12/24 17:26:25 127.0.0.1:7539
hello2


[17:26:37]	 [recv,2604] [WinError 10054] 遠程主機強迫關閉了一個現有的連接。

  

#客戶端2
>>> hello2
>>> [17:26:25]	 [recv,4044] 2017/12/24 17:26:25 127.0.0.1:7539
hello2


[17:26:37]	 [recv,4044] [WinError 10054] 遠程主機強迫關閉了一個現有的連接。

  

以上例子在客戶端,如果服務端主動斷開,客戶端需要異常處理。

[Python 網絡編程] TCP Client (四)