zoukankan      html  css  js  c++  java
  • Python之socket_tcp

    1.1socket编程之tcp编程

    """
    socket类型
    sock_stream 面向连接的流套接字,默认值 tcp协议
    sock_dgram  无连接的数据报文套接字,udp协议
    """
    import socket
    s = socket.socket()
    s.bind(('127.0.0.1',9999))  #bind接受一个2元祖
    s.listen()
    """
    Accept a connection. The socket must be bound to an address and listening for connections.
    The return value is a pair (conn, address) where conn is a new socket object usable to send and receive data on the connection, 
    and address is the address bound to the socket on the other end of the connection
    """
    new_socker,info = s.accept() #只接受一个client请求,阻塞
    data=new_socker.recv(1024)  #阻塞
    print(data)
    print(type(data))
    new_socker.send('back {}'.format(data).encode())
    s.close()  

     例子

    有阻塞就要尽量放到线程中去执行,不要影响主线程
    import socket
    server = socket.socket()
    server.bind(('127.0.0.1',9999))
    server.listen()
    '''
    建立一个client,socket连接之后,只能发一次数据
    在accept出阻塞,需要建立第二个连接
    '''
    while True:
        new_socket,ip = server.accept()
        data = new_socket.recv(1024)
        new_socket.send('ck test {}'.format(data).encode())

     socket群聊实例

    import socket
    import threading
    
    class ChatTcpServer:
        def __init__(self,ip,port):
            self.ip = ip
            self.port = port
            self.cliets = {}
            #实例化就创建一个socket对象
            self.socket = socket.socket()
        def start(self):
            self.socket.bind((self.ip,self.port))
            self.socket.listen()
            threading.Thread(target=self._accept,name="_aceept").start()
    
        def _accept(self):  #只开启一个accept线程,连接产生的new_socket负责和线程receive同信
            while True:
                print('1~~~~~~~~~~~~~~~~~~~',threading.enumerate())
                new_socket,raddr = self.socket.accept()    #阻塞主线程,所以开启一个工作线程receive
                threading.Thread(target=self._receive,name="reveive",args=(new_socket,)).start()
                self.cliets[new_socket] = raddr
                print(self.cliets)
                print(type(self.cliets.keys()))
    
        def _receive(self,new_socket): #客户端连接几个socket 就开几个receive线程
            while True:
                print('2~~~~~~~~~~~~~~~~~~~',threading.enumerate())
                data = new_socket.recv(1024)               #阻塞
                for k in self.cliets.keys():
                    k.send('ack {}'.format(data).encode())
    
        def stop(self):
            for s in self.cliets.values():
                s.close()
            self.socket.close()
    
    
    if __name__ == '__main__':
        cs = ChatTcpServer('127.0.0.1',9999)
        cs.start()
        # print('3~~~~~~~~~~~~~~~~~~~',threading.enumerate())
        # cs.stop()
    problem:there has a error when cliens exit
    ConnectionAbortedError
    import socket
    import threading
    
    class ChatTcpServer:
        def __init__(self,ip,port):
            self.ip = ip
            self.port = port
            self.cliets = {}
            #实例化就创建一个socket对象
            self.socket = socket.socket()
            self.event = threading.Event()
        def start(self):
            self.socket.bind((self.ip,self.port))
            self.socket.listen()
            threading.Thread(target=self._accept,name="_aceept").start()
    
        def _accept(self):  #只开启一个accept线程,连接产生的new_socket负责和线程receive同信
            while not self.event.is_set():
                new_socket,raddr = self.socket.accept()    #阻塞主线程,所以开启一个工作线程receive
                threading.Thread(target=self._receive,name="reveive",args=(new_socket,)).start()
                self.cliets[new_socket] = raddr
                print(self.cliets)
                print(type(self.cliets.keys()))
    
        def _receive(self,new_socket): #客户端连接几个socket 就开几个receive线程
            while not self.event.is_set():
                data = new_socket.recv(1024)               #阻塞
                for k in self.cliets.keys():
                    k.send('ack {}'.format(data).encode())
    
        def stop(self):
            if self.cliets:
                for s in self.cliets.values():
                    s.close()
            self.socket.close()
            self.event.set()
    
    
    if __name__ == '__main__':
        cs = ChatTcpServer('127.0.0.1',9999)
        cs.start()
        while True:
            cmd = input("please set stop:>>>")
            if cmd == 'quit':
                cs.stop()
                break
    以上版本服务端可以断开连接,但是客户端断开连接抛出异常 

    增加客户端断开命令

    客户端主动断开连接的问题,服务端知道自己何时断开
    如果是客户端断开服务器不知道,所有好的做法客户端发出特殊消息通知服务器断开连接,但是客户端主动断开服务端主动发送一个空消息,超时返回异常,捕获异常并清理连接,即使为客户端提供了断开命令,也不能保证客户端会使用它断开连接,还是要增加这个退出功能
    
        def _receive(self,new_socket): #客户端连接几个socket 就开几个receive线程
            while not self.event.is_set():
                data = new_socket.recv(1024)               #阻塞
                if data == b"quit":
                    self.cliets.pop(new_socket)
                    new_socket.close()
                    break
                for k in self.cliets.keys():
                    k.send('ack {}'.format(data).encode())

     socket常用方法

    socket = socket.socket()
    socket.recv(1024) bufsize=1024 获取数据,默认是阻塞状态
    socket.recvfrom(1024)   获取数据,返回一个二元组(bytes,address)
    socket.recv_into(buffer=1024,nbytes=10,flags=None) 获取到nbytes的数据后,存储到buffer中,如果nbytes没有指定或者0,将buffer大小的数据存入buffer中,返回接收的字节数
    socket.send(bytes)  tcp发送设备
    socket.sendall(bytes)   tcp发送全部设备,成功返回None
    

     Makefile

    socket.socket().makefile(mode='r',buffering=None)   创建一个与该套接字相关联的文件对象,将recv方法看作读方法,将send方法看作写方法
    
    socket.getpeername()    返回连接套接字的远程地址,返回值通常是元组(ipaddr,port)
    sockete.getsockname()    返回套接字自己的地址,通常是一个元祖(ipaddr,port)
    socket.setbloking(flag)    如果flag为0则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)
    非阻塞模式下,如果调用recv没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常
    socket.settimeout(value)    设置套接字操作的超时期,timeout是一个浮点数单位秒
    值为none表示没有超时期,一般超时期应该在刚创建套接字时设置,因为他们可能用于连接的操作(connect())

     read和readline(行读取,包括换行符)

    import socket
    socket = socket.socket()
    socket.bind(('127.0.0.1',9999))
    socket.listen()
    
    while True:
        new_socket,raddr = socket.accept()
        f = new_socket.makefile('rw')
        print(f)
        line = f.readline()  #Read and return one line from the stream
        line = f.read(10)   #Read up to n bytes from the object and return them
        #只有输入十个字节才会return
        print(line)
        f.write('ack {}'.format(line))
        f.flush()
    f.close()

    client编写

    import socket
    socket = socket.socket()
    socket.bind(('127.0.0.1',9999)) #server ip
    socket.listen()
    
    new_socket,raddr = socket.accept()
    data = new_socket.recv(1024)
    print(data)
    new_socket.send('ack {}'.format(data).encode())
    import socket
    import threading
    import datetime
    import logging
    
    FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
    logging.basicConfig(level=logging.INFO,format=FORMAT)
    
    class ChatClient:
        def __init__(self,ip='127.0.0.1',port=9999):
            self.ip = ip
            self.port = port
            self.sock =socket.socket()
            self.event = threading.Event()
    
        def start(self):
            self.sock.connect((self.ip,self.port))
            self.send('i am ready')
            threading.Thread(target=self.receive,name='receive').start()
    
    
        def receive(self):
            while not self.event.is_set():
                try:  #连接不成功,捕获异常
                    data = self.sock.recv(1024)
                    logging.info(data)
                except Exception as e:
                    logging.info(e)
    
        def send(self,msg:str):
            self.sock.send('{}'.format(msg).encode())
    
    
        def stop(self):
            self.sock.close()
            self.event.set()
            logging.info('Client stops')
    
    def main():
        cc = ChatClient()
        cc.start()
        while True:
            cmd = input("please set a number:")
            if cmd.strip() == b'quit':
                cc.stop()
                break
            cc.send(cmd)
    
    if __name__ == '__main__':
        main()
    本文为原创文章,转载请标明出处
  • 相关阅读:
    学习篇之函数就是对象
    @Controller和@RestController的区别
    maven 阿里云 国内镜像 中央仓库
    IntelliJ IDEA使用教程
    解决“无法从套接字读取更多数据”
    at java.util.Arrays.copyOfRange(Arrays.java:3209)导致的java.lang.OutOfMemoryError: Java heap space 错误的解决办法
    maven中引入ojdbc包
    Oralce增加表空间
    Web服务框架发展与REST服务开发
    Oralce解锁表
  • 原文地址:https://www.cnblogs.com/harden13/p/9189869.html
Copyright © 2011-2022 走看看