zoukankan      html  css  js  c++  java
  • Python-TCP编程

    1、Socket介绍:

      socket 套接字

      Python中提供socket.py标准库,非常底层的接口库。

      Socket 是一种通用的网络编程接口,和网络底层没有一一对应的关系

      协议族:

        AF:address family, 用于socket()第一个参数

      

      Socket 类型

      

    2、TCP编程:

      Socket编程,需要两端,一般来说,需要一个服务器端,一个客户端,服务器端为server,客户端为client

    2.1、TCP服务器端编程:

      服务器端编程步骤:

      • 创建Socket对象
      • 绑定IP 地址 address, 和端口 port,bind() 方法,IPv4地址为一个二元组(‘IP地址字符串’,port)
      • 开始监听,将在指定的ip 端口上监听,listen()方法
      • 获取用于传递数据的socket 对象

          socket.accept() ---> (socket.object, address info)

          accept 方法 阻塞等待客户端建立连接,返回一个新的额socket对象和客户端地址的二元组

          地址是远程客户端的地址,IPV4 中他是一个二元组(clientaddr,port)

          • 接受数据:recv( bufsize [, flags])使用了缓冲区接受数据
          • 发送数据:send(bytes)发送数据

      

       只有一个client 请求的 样例:     

     1 import socket
     2 
     3 # 1、创建套接字
     4 socket = socket.socket()
     5 
     6 # 2、绑定ip 和 port
     7 ip = '127.0.0.1'
     8 port = 9999
     9 laddr = (ip, port)
    10 
    11 socket.bind(laddr)
    12 
    13 # 3、监听 套接字
    14 socket.listen()
    15 
    16 # 4、等待接受 客户端数据,并生产新的socket,用于数据交互
    17 data = socket.accept()
    18 newsocket, raddr = data
    19 
    20 # 5、接受数据
    21 
    22 msg = newsocket.recv() # 收到的是字节类型
    23 
    24 # 6、发送数据
    25 newsocket.send(msg) # 发送也得是字节类型
    26 
    27 # 7、当前socket 通信结束,可以关闭
    28 newsocket.close()
    29 
    30 # 8、关闭资源 ,这里关闭的是 创建连接时的 socket
    31 socket.close()
    服务器端基本样例

      newsocket:  

    <socket.socket fd=212, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 50287)>

      raddr:远程套接字  

    ('127.0.0.1', 50287)  

      recv(1024):缓冲区大小一般是1024 的整数倍

      socket.accept():阻塞直到 和客户端 成功建立连接,返回一个socket对象和客户端地址。

      newsocket.recv(1024):连接成功后,没有收到数据,会阻塞在这里。

        注:    

          TCP的服务端编程:
             端口是给进程的,线程共享资源。
            通过监听来获得端口,对端口进行暴露 ,同时监听
             一个socket 是为了连接,创建新的socket进行资源通信
      
      win cmd:
      netstat -anp tcp | findstr 9999 (win的管道)
      -b:显示进程
      Linux:
         一般使用ss
      测试:
        1、服务器端,主线程是用来做其他工作,所以,不能让accept占据主线程,处于阻塞状态
        2、每个客户端应该都在自己的线程中工作,否则会recv阻塞,影响别人
        3、为了方便的将信息通知给所有的客户端,所以某个人发出的信息,必须都发给其他人一份,所以这里通过字典,将所有newsocket收集起来
        4、提供退出机制,因为使用 sindows上的调试工具,直接退出,会返回一个 b'',所以这里使用 b'quit' 或 b''退出客户端
        5、主线程(server),关闭的时候,线程也依次结束,避免占用资源。
        6、可以提供日志功能,将日志输出到文件
      
     1 import socket
     2 import logging
     3 import threading
     4 
     5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
     6 logging.basicConfig(format=FORMAT, level=logging.INFO)
     7 
     8 class ChatServer:
     9     def __init__(self, ip='127.0.0.1', port=9999):
    10         self.socket = socket.socket()
    11         self.laddr = (ip, port)
    12         self.event = threading.Event()
    13         self.clients = {}
    14 
    15     def start(self):
    16         self.socket.bind(self.laddr)
    17         self.socket.listen()
    18         threading.Thread(target=self.accept, name='accept').start()
    19 
    20     def accept(self):
    21         while not self.event.is_set():
    22             newsocket, raddr = self.socket.accept()
    23             self.clients[raddr] = newsocket
    24             threading.Thread(target=self.recv,args=(newsocket, raddr)).start()
    25 
    26     def recv(self, s, raddr):
    27         while not self.event.is_set():
    28             data = s.recv(1024)
    29             logging.info(data)
    30 
    31             if data.strip() == b'quit' or data == b'':
    32                 self.clients.pop(raddr)
    33                 s.close()
    34                 break
    35             for s in self.clients.values():
    36                 logging.info(data)
    37                 msg = 'msgs is that  {}'.format(data.decode())
    38                 s.send(msg.encode())
    39 
    40 
    41     def stop(self):
    42         for s in self.clients.values():
    43             s.close()
    44         self.socket.close()
    45         self.event.set()
    46 
    47 
    48 cs = ChatServer()
    49 cs.start()
    50 
    51 while True:
    52     cmd = input('>>')
    53     if cmd.strip() == 'quit':
    54         cs.stop()
    55         break
    56     logging.info(threading.enumerate())
    服务器端代码
      socket常用方法:
       

       

    2.2、MakeFile:

      socket.makefile(mode='r', buffering=None, * , encoding=None, error=None, newline=None)

      创建一个与该套接子相关联的文件对象,将recv 方法看做读方法,将send 方法看做是写方法。

      测试:显示读和写

     1 import socket
     2 sock = socket.socket()
     3 
     4 ip = '127.0.0.1'
     5 port = 9999
     6 
     7 addr = (ip, port)
     8 
     9 sock.bind(addr)
    10 sock.listen()
    11 
    12 
    13 s, _ = sock.accept()
    14 print(s)
    15 
    16 # 这儿权限只能 r w a 不能 出现 +
    17 # 创建一个 类文件对象
    18 f = s.makefile(mode='rw')
    19 
    20 line = f.read(10) # recv
    21 print(line )
    22 
    23 f.write(' -----{}-----'.format(line))
    24 f.flush()# 写完之后 flush一下
    简单测试 read
     1 import socket
     2 sock = socket.socket()
     3 
     4 ip = '127.0.0.1'
     5 port = 9999
     6 
     7 addr = (ip, port)
     8 
     9 sock.bind(addr)
    10 sock.listen()
    11 
    12 
    13 s, _ = sock.accept()
    14 print(s)
    15 
    16 # 这儿权限只能 r w a 不能 出现 +
    17 # 创建一个 类文件对象
    18 f = s.makefile(mode='rw')
    19 
    20 line = f.readline(10) # recv
    21 print(line )
    22 
    23 f.write(' -----{}-----'.format(line))
    24 f.flush()# 写完之后 flush一下
    readline

       readline 是遇到回车换行才 输出,read 是 够了字符数,输出 

      s   :    <socket.socket fd=212, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 52129)>
      f   :     <_io.TextIOWrapper mode='rw' encoding='cp936'>

      测试:将群聊服务器端 改为makefile

     1 import socket
     2 import logging
     3 import threading
     4 
     5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
     6 logging.basicConfig(format=FORMAT, level=logging.INFO)
     7 
     8 class ChatServer:
     9     def __init__(self, ip='127.0.0.1', port=9999):
    10         self.socket = socket.socket()
    11         self.laddr = (ip, port)
    12         self.event = threading.Event()
    13         self.clients = {}
    14 
    15     def start(self):
    16         self.socket.bind(self.laddr)
    17         self.socket.listen()
    18         threading.Thread(target=self.accept, name='accept').start()
    19 
    20     def accept(self):
    21         while not self.event.is_set():
    22             newsocket, raddr = self.socket.accept()
    23 
    24             f  = newsocket.makefile('rw')
    25 
    26             self.clients[raddr] = f
    27             threading.Thread(target=self.recv,args=(newsocket, f, raddr)).start()
    28 
    29     def recv(self, s,f, raddr):
    30         while not self.event.is_set():
    31             data = f.readline(10)
    32             logging.info(data)
    33 
    34             if data.strip() == 'quit' or data == b'':
    35                 self.clients.pop(raddr)
    36                 f.close() # 只关文件描述符,可能完全断不开,所以把socket也关一下
    37                 s.close()
    38                 break
    39             for f in self.clients.values():
    40                 logging.info(data)
    41                 f.write(data)
    42                 f.flush()
    43 
    44 
    45     def stop(self):
    46         for s in self.clients.values():
    47             s.close()
    48         self.socket.close()
    49         self.event.set()
    50 
    51 
    52 cs = ChatServer()
    53 cs.start()
    54 
    55 while True:
    56     cmd = input('>>')
    57     if cmd.strip() == 'quit':
    58         cs.stop()
    59         break
    60     logging.info(threading.enumerate())
    改为makefile

    2.2、TCP客户端编程

      客户端编程步骤:

      • 创建socket对象
      • 连接到远程服务端的IP 和port ,connect()方法
      • 传输数据
        • 使用send, recv方法,发送数据,接受数据

        关闭连接,释放资源

      

    2.3、一个群聊程序:注意:群聊,所以,不发信息,也能recv信息,所以recv放到一个线程中,一直执行

      服务器端程序:  

     1 import socket
     2 import logging
     3 import threading
     4 
     5 FORMAT = '%(asctime)s %(thread)s %(threadName)s %(message)s'
     6 logging.basicConfig(format=FORMAT, level=logging.INFO)
     7 
     8 class ChatServer:
     9     def __init__(self, ip='127.0.0.1', port=9999):
    10         self.socket = socket.socket()
    11         self.laddr = (ip, port)
    12         self.event = threading.Event()
    13         self.clients = {}
    14 
    15     def start(self):
    16         self.socket.bind(self.laddr)
    17         self.socket.listen()
    18         threading.Thread(target=self.accept, name='accept').start()
    19 
    20     def accept(self):
    21         while not self.event.is_set():
    22             newsocket, raddr = self.socket.accept()
    23 
    24             f  = newsocket.makefile('rw')
    25 
    26             self.clients[raddr] = f
    27             threading.Thread(target=self.recv,args=(newsocket, f, raddr)).start()
    28 
    29     def recv(self, s,f, raddr):
    30         while not self.event.is_set():
    31             data = f.readline(10)
    32             logging.info(data)
    33 
    34             if data.strip() == 'quit' or data == b'':
    35                 self.clients.pop(raddr)
    36                 f.close() # 只关文件描述符,可能完全断不开,所以把socket也关一下
    37                 # s.close()
    38                 break
    39             for f in self.clients.values():
    40                 logging.info(data)
    41                 f.write(data)
    42                 f.flush()
    43 
    44 
    45     def stop(self):
    46         for s in self.clients.values():
    47             s.close()
    48         self.socket.close()
    49         self.event.set()
    50 
    51 
    52 cs = ChatServer()
    53 cs.start()
    54 
    55 while True:
    56     cmd = input('>>')
    57     if cmd.strip() == 'quit':
    58         cs.stop()
    59         break
    60     logging.info(threading.enumerate())
    server

      客户端程序:

     1 import socket
     2 import threading
     3 
     4 class ChatClient:
     5     def __init__(self, ip='127.0.0.1', port=9999):
     6         self.socket = socket.socket()
     7         self.event = threading.Event()
     8         self.raddr = (ip, port)
     9 
    10     def start(self):
    11         self.socket.connect(self.raddr)
    12 
    13         self.send('hello I am client')
    14         # while not self.event.is_set():
    15             # data = self.socket.recv(1024)
    16             # print(data)
    17         threading.Thread(target=self.recv).start()
    18 
    19     def send(self, msg):
    20         self.socket.send(msg.encode())
    21 
    22     def recv(self):
    23         while not self.event.is_set():
    24             data = self.socket.recv(1024)
    25             print(data)
    26 
    27     def stop(self):
    28         self.socket.close()
    29         self.event.set()
    30 
    31 cc = ChatClient()
    32 cc.start()
    33 
    34 while True:
    35     cmd = input('>>')
    36     if cmd.strip() == 'quit':
    37         cc.stop()
    38         break
    39     cc.send(cmd)
    client

      server:

      client 1

      client 2:

    为什么要坚持,想一想当初!
  • 相关阅读:
    Thinkphp5.0实战开发一------命名空间详解
    软件测试技术实验二
    软件测试技术作业3---PrintPrimes()
    软件测试技术实验一
    Github使用教程(二)------ Github客户端使用方法
    Github使用教程(一)------ 初识Github
    软件测试技术作业2
    软件测试作业1 — 令我印象最深的BUG
    Github网站加载不完全,响应超时,如何解决
    利用puppeteer实现PDF文件导出
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9888690.html
Copyright © 2011-2022 走看看