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:

    为什么要坚持,想一想当初!
  • 相关阅读:
    有点忙啊
    什么是协程
    HDU 1110 Equipment Box (判断一个大矩形里面能不能放小矩形)
    HDU 1155 Bungee Jumping(物理题,动能公式,弹性势能公式,重力势能公式)
    HDU 1210 Eddy's 洗牌问题(找规律,数学)
    HDU1214 圆桌会议(找规律,数学)
    HDU1215 七夕节(模拟 数学)
    HDU 1216 Assistance Required(暴力打表)
    HDU 1220 Cube(数学,找规律)
    HDU 1221 Rectangle and Circle(判断圆和矩形是不是相交)
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9888690.html
Copyright © 2011-2022 走看看