[Python 网络编程] TCP Client (四)

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] 远程主机强迫关闭了一个现有的连接。

  

以上例子在客户端,如果服务端主动断开,客户端需要异常处理。

时间: 2024-08-29 13:14:51

[Python 网络编程] TCP Client (四)的相关文章

python网络编程——TCP

<pre name="code" class="python"> 一.网络通信模式 对于网络通信,现在遵循的是TCP/IP协议组. 而现在设计的通信模式中,主要使用的是客户端/服务器编程,所谓的客户端就是我们用户所用的软件,而服务端则是程序开发人员根据客户端的需要设计的服务模式,为了是满足客户端的要求,实现和客户端进行正常通信. 二.套接字:通信的端点 对于tcp/Ip协议来说,它是一个多层协议族,分别是物理层,数据链路层,网络层,传输层,应用层.对于网

python网络编程-TCP协议中的三次握手和四次挥手(图解)

建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源.Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了. 那如何断开连接呢?简单的过程如下: [注意]中断连接端可以是Client端,也可以是Server端. 假设Client端发起中断连接请求,也就是发送FIN报文.Server端接到FIN报文后,

python网络编程 - tcp

网络编程 低级别的网络服务 高级别的网络服务 socket又称“套接字”,应用程序通过“套接字”向网络发出请求或者应答网络请求,使主机间或者一台计算机上的进程间可以通讯. python代码实现 服务端 流程 1.socket创建一个套接字 2.bind绑定ip和port 3.listen 使套接字变为可以被动链接(默认创建的套接字是主动去链接别人的) 4.accept 等待客户端的链接(accept和客户端的connect是一对,服务器的accept只响应客户端的connect) 5.send/

Python网络编程—TCP套接字之HTTP传输

HTTP协议 (超文本传输协议) 1.用途 : 网页获取,数据的传输 2.特点: 应用层协议,传输层使用tcp传输 简单,灵活,很多语言都有HTTP专门接口 无状态,协议不记录传输内容 http1.1 支持持久连接,丰富了请求类型 3.网页请求过程 客户端(浏览器)通过tcp传输,发送http请求给服务端 服务端接收到http请求后进行解析 服务端处理请求内容,组织响应内容 服务端将响应内容以http响应格式发送给浏览器 浏览器接收到响应内容,解析展示 HTTP请求(request) 1.请求行

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

Python网络编程02/基于TCP协议的socket简单的通信

目录 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 2.socket 2.1 socket套接字 2.2 基于TCP协议的socket简单通信 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 1.单播:单独联系某一个人 2.广播:给所有人发送消息(群发) 3.比特流:bit就是0101跟水流一样的源源不断的发送01010101 4.以太网协议:将数据进行分组:一组称之为一帧,数据报 head|data head:18字节:

python 网络编程 (二)---tcp

异常 python的socket模块实际上定义了4种可能出现的异常: 1)与一般I/O 和通信问题有关的socket.error; 2)与查询地址信息有关的socket.gaierror; 3)与其他地址错误有关的socket.herror; 4)与在一个socket上调用settimeout()后,处理超时有关的socket.timeout; import socket, sys, time host = sys.argv[1] textport = sys.argv[2] filename

python网络编程,通过服务名称和会话类型(tcp,udp)获取端口号,简单的异常处理

作为一个php程序员,同时有对网络方面感兴趣,php就比较蛋疼了,所以就抽了些时间看python 之前学python基础因为工作原因,断断续续的看了个基础,差不多是可以写代码了 最近在看<python网络编程基础>,准备是边实践边学习了,对书上的一个例子做了个复制 cli下运行的代码 1 #! /usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # ^设置编码:代码中有中午会导致编译错误 4 5 # 引入socket,sys模块 6 import so

Python 网络编程(二)

Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单的仿ssh的socket程序,实现的功能为客户端发送命令,服务端接收到客户端的命令,然后在服务器上通过subrocess模块执行命令,如果命令执行有误,输出内容为空,则返回"command error"的语句给客户端,否则将命令执行的结果返回给客户端 服务端 1 2 3 4 5 6 7 8