• chapter12.1、socket编程,tcp实现通讯


    网络编程

    Socket编程

    socket套接字

    端到端编程,建立了一条数据传输的通道

    网络编程的基础,所有的操作系统都支持socket

    socket是一种通用的网路编程接口,不需要关心协议层,只是打通了一条通道,和网络层次没有一一对应关系。

    IPC,RPC,最底层也是socket实现

    协议族

    名称 含义
    AF_INET IPV4
    AF_INET6 IPV6
    AF_UNIX Unix Domain Socket,windows没有
     
     
     
     

    socket 类型

    名称 含义
    SOCK_STREAM 面向连接的流套接字,默认值,TCP协议
    SOCK_DGRAM 无连接的数据报文套接字,UDP协议,效率高,易产生乱序,丢包问题
     
     
     
     
     

    TCP编程

    服务端server,客户端client,cs编程,

    服务器编程步骤

    1、创建Socket对象

    2、绑定IP地址和端口,IP负责地址,端口管应用程序

    端口是进程的,不是线程的,只是线程共享进程的资源

    3、监听,暴露端口,将指定的IP端口开始监听

    4、socket accept 获取传送数据的socket对象

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

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

    • recv(bufsize[,flags])  使用缓冲区接收数据

    • send(bytes)  发送数据

    注意:两次绑定同一个端口会报错,操作系统级别的错误

    Windows报错:  通常每个套接字地址(协议/网络地址/端口)只允许使用一次。

    #1 socket
    sevrer = socket.socket()
    
    #2 bind
    ip = '127.0.0.1'
    port = 9999
    addr = (ip,port)
    server.bind(addr)
    
    #3 listen
    server.listen()
    
    #4 accept
    s1, info = server.accept()  #阻塞等待client连接
    
    #5 recv,send
    data = server.recv(1024)
    s1.send("hello, I'm server")
    
    #6 close
    server.close()

    netstat Windows使用 b 显示进程,要用管理员权限 windows下

    ss -tanl l表示监听,t tcp协议,a全部,n数字显示端口 Linux下

    windows下的管道也可以使用,findstr是过滤方法

    群聊程序TCP协议服务器端简单实现

    TCPChatserver
     1 import socket
     2 import threading
     3 import logging
     4 
     5 FORMAT = "%(thread)d %(threadName)s %(asctime)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.sock = socket.socket()
    11         self.addr = (ip,port)
    12         self.clients = {}
    13         self.event = threading.Event()
    14 
    15     def start(self):#开始监听
    16         self.sock.bind(self.addr)
    17         self.sock.listen()
    18         #accept会阻塞主线程,开线程跑
    19         threading.Thread(target=self.accept,name="accept").start()
    20 
    21     def accept(self):#多人连接
    22         while not self.event.is_set():
    23             try:
    24                 sock,raddr = self.sock.accept()#阻塞等待
    25             except Exception:
    26                 self.stop()
    27                 break
    28             self.clients[raddr] = sock#记录客户端
    29             logging.info(raddr)
    30             #开启新线程,一个新的socket对象处理单个client
    31             threading.Thread(target=self.recv,args=(sock,raddr),name="r-{}".format(raddr)).start()
    32 
    33 
    34     def recv(self,sock,raddr):#接受数据
    35         while not self.event.is_set():
    36             try:
    37                 data = sock.recv(1024).strip()#阻塞等待
    38             except Exception:
    39                 break
    40             logging.info(":{}".format(data.decode()))
    41             #client退出机制
    42             if data.strip() == b"quit" or data ==b"":
    43                 print(data,"~~~~~~~~~~")
    44                 self.clients.pop(raddr)
    45                 sock.close()
    46                 break
    47 
    48             msg = "{}".format(data)
    49             msg = msg.encode()
    50             for client in self.clients.values():
    51                 client.sendall(msg)
    52 
    53     def stop(self):#停止服务
    54         for c in self.clients.values():
    55             c.close()
    56         self.sock.close()
    57         self.event.set()
    58 
    59 def main():
    60     server = ChatServer()
    61     server.start()
    62 
    63     while True:
    64         cmd = input(">>>")
    65         if cmd == "quit":
    66             server.stop()
    67             threading.Event().wait(3)
    68             break
    69         logging.info(threading.enumerate())#观察线程变化
    70 
    71 if __name__ == '__main__':
    72     main()

    只是实现主要功能,且现在不用该方法了

    socket 方法

     
    名称 含义
    socket.recv(bufsize[, flags])  获取数据。默认是阻塞的方式
    socket.recvfrom(bufsize[, flags])  获取数据,返回一个二元组(bytes, address)
    socket.recv_into(buffer[, nbytes[,flags]]) 获取到nbytes的数据后,存储到buffer中。如果nbytes没有指定或0,将buffer大小的数据存入buffer中。返回接收的字节数。
    socket.recvfrom_into(buffer[,nbytes[, flags]]) 获取数据,返回一个二元组(bytes, address)到buffer中
    socket.send(bytes[, flags]) TCP发送数据
    socket.sendall(bytes[, flags]) TCP发送全部数据,成功返回None
    socket.sendto(string[,flag],address) UDP发送数据
    socket.sendfile(file, offset=0,count=None) 发送一个文件直到EOF,使用高性能的os.sendfile机制,返回发送的字节数。如果win下不支持sendfile,或者不是普通文件,使用send()发送文件。offset告诉起始位置。3.5版本开始
    socket.getpeername() 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)
    socket.getsockname() 返回套接字自己的地址。通常是一个元组(ipaddr,port)
    socket.setblocking(flag) 如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常
    socket.settimeout(value) 设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())
    socket.setsockopt(level,optname,value) 设置套接字选项的值。比如缓冲区大小。太多了,去看文档。不同系统,不同版本都不尽相同
     
     

    makefile方法

    类文件对象socket会占文件描述符,不用要归还
    import socket
    import threading
    import logging
    
    FORMAT = "%(asctime)s %(thread)d %(message)s"
    logging.basicConfig(format=FORMAT,level=logging.INFO)
    
    class ChatTCPServer:
        def __init__(self,ip="127.0.0.1",port=9999):
            self.sock = socket.socket()
            self.addr = ip,port
            self.event = threading.Event()
            self.clients = {}
    
        def start(self):
            self.sock.bind(self.addr)
            self.sock.listen()
            threading.Thread(target=self.accept,name="accept").start()
    
        def accept(self):
            while not self.event.is_set():
                try:
                    s, client = self.sock.accept()
                    f = s.makefile("rw")
                except:
                    self.stop()
                    break
                self.clients[client] = f
    
                threading.Thread(target=self.recv,args=(f, client,s),name="client-{}".format(client[1])).start()
                logging.info("{} login".format(client))
    
        def recv(self,f,raddr,s):
    
            while not self.event.is_set():
                try:
                    msg = f.readline().strip()
                except Exception as e:
                    logging.error(e)
                    self.clients.pop(raddr)
                    f.close()
                    break
                logging.info(msg)
                if msg == "quit" or "":
                    self.clients.pop(raddr)
                    f.close()
                    break
    
                for f1 in self.clients.values():
                    f1.write(msg)
                    f1.flush()
    
        def stop(self):
            for client in self.clients.values():
                client.close()
            self.sock.close()
            self.event.set()
    
    
    def main():
        server = ChatTCPServer()
        server.start()
        while True:
            cmd = input(">>>")
            if cmd =="quit":
                server.stop()
                threading.Event().wait(3)
                break
            logging.info(threading.enumerate())
    
    if __name__ == '__main__':
        main()
    ChatTCPServer

     client 客户端实现

     创建socket对象

    连接到远程服务端的ip,port,connect方法

    传输数据

    使用send,recv方法接受,发送数据

     关闭连接,释放资源

     1 import socket
     2 import threading
     3 import logging
     4 import datetime
     5 
     6 logging.basicConfig(format="%(asctime)s %(thread)s %(message)s",level=logging.INFO)
     7 
     8 class ChatTcpClient:
     9     def __init__(self,ip="127.0.0.1",port=9999):
    10         self.sock = socket.socket()
    11         self.event = threading.Event()
    12         self.raddr = ip,port
    13 
    14     def start(self):
    15         self.sock.connect(self.raddr)
    16         self.sock.send(b"I'm ready")
    17         threading.Thread(target=self.recv,name='recv').start()
    18 
    19     def recv(self):
    20         while not self.event.is_set():
    21             try:
    22                 data = self.sock.recv(1024).decode()
    23             except Exception as e:
    24                 logging.info(e)
    25                 break
    26             msg = "{:%Y%m%d %H:%M:%S} {}:{}
    {}".format(datetime.datetime.now(),*self.raddr,data.strip())
    27             logging.info(msg)
    28 
    29     def send(self,msg:str):
    30         data = "{}
    ".format(msg.strip()).encode()
    31         self.sock.send(data)
    32 
    33     def stop(self):
    34         self.sock.close()
    35         self.event.wait(3)
    36         self.event.set()
    37         logging.info("Client stop")
    38 
    39 def main():
    40     cc = ChatTcpClient()
    41     cc.start()
    42     while True:
    43         cmd = input(">>>")
    44         if cmd == "quit" or "":
    45             cc.stop()
    46             break
    47         cc.send(cmd)
    48 
    49 if __name__ == '__main__':
    50     main()
    View Code
     
     
     
     
     
     
     
     
  • 相关阅读:
    C++重载运算符
    std::vector
    new、delete、动态数组初始化、野指针、解引用、内存池、重载new和delete、内存泄漏等,释放崩溃
    C++匿名函数lambda
    缺少wntdll.pdb
    map、multimap和unordered_map(hash_map)以及unorderd_multimap
    分享一个双击后编辑并AJAX提交保存的脚本
    jquery.cookie.js操作cookie
    MVC获得某个View 或者 PartialView 生成的字符串
    c#中using 和new
  • 原文地址:https://www.cnblogs.com/rprp789/p/9901821.html
Copyright © 2020-2023  润新知