• socket通讯实例与TCP/UDP的区别


    一、socket代码实例

    1.简单的socket通讯:

    服务端代码实例:

    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket对象 声明协议类型
    sock.bind(("localhost", 9909))  # 绑定地址 端口
    
    sock.listen(5)  # 监听,5代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
    
    conn, addr = sock.accept()  # 接收客户端发来的连接请求 组成一个元祖
    
    data = conn.recv(1024)  # 接收消息
    
    print(data)
    
    conn.send(data.upper())  # 发送消息
    
    conn.close()  # 关闭连接
    
    sock.close()  # 关闭socket
    

    客户端代码实例:

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 生成客户端实例
    
    client.connect(("127.0.0.1", 9909))  # 连接地址端口
    
    client.send("hello word".encode("utf8"))  # 发送消息给服务端
    
    data = client.recv(1024)  # 接收消息
    client.close()  # 关闭客户端
    print(data)
    

    2.循环收发数据socket通讯:

    服务端

    import socket
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(("localhost", 9909))
    
    sock.listen(5)
    while True:
        conn, addr = sock.accept()  # 有加上一个死循环(连接循环)
        while True:
            try:   # 适应于window系统
                data = conn.recv(1024)  # 通讯循环
                if not data: break  # 适用于linux系统
                print(data)
    
                conn.send(data.upper())
            except ConnectionResetError:
                break
    
        conn.close()
    
    sock.close()
    

    客户端

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    client.connect(("127.0.0.1", 9909))
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        client.send(msg.encode("utf8"))
        data = client.recv(1024)
        print(data.decode('utf8'))
    client.close()
    

    3.多线程循环收发数据socket通讯:

    服务端

    import socket
    import threading
    
    
    def handler(conn):
        while True:  # 通讯循环
            try:
                data = conn.recv(1024)
                if not data:
                    break  # 适用于linux系统
                print(data)
    
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    
    
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("localhost", 8889))
    
    sock.listen(5)
    while True:
        conn, addr = sock.accept()  # 连接循环
        p = threading.Thread(target=handler, args=(conn,))  # 每来一个conn连接开启一个线程,交给handler去执行 
        p.start()
    
    sock.close()
    

    服务端

    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    client.connect(("127.0.0.1", 8889))
    while True:
        msg = input(">>>:").strip()
        if not msg:
            continue
        client.send(msg.encode("utf8"))
        data = client.recv(1024)
        print(data.decode('utf8'))
    client.close()
    

    4.问题解决

    有的同学在重启服务端时可能会遇到

    这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)

    解决方法1:

    sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #一行代码搞定,写在bind之前
    sock_server.bind((HOST, PORT))
    

    解决方法2:

    发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
    vi /etc/sysctl.conf
    
    编辑文件,加入以下内容:
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30
    
    然后执行 /sbin/sysctl -p 让参数生效。
    
    net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    
    net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
    

    5.基于UDP协议的套接字通讯

    服务端

    import socket
    
    server_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    
    server_udp.bind(("127.0.0.1", 9900))
    
    while True:
        # 循环链接,拿到的是data数据,还有addr地址
        # 区别于TCP协议
        data, client_addr = server_udp.recvfrom(1024)  
        print(data)
        # 发送消息send_to  消息内容+目标addr地址
        server_udp.sendto(data.upper(), client_addr) 
        print(data)
    

    客户端

    from socket import *
    
    client = socket(AF_INET, SOCK_DGRAM)
    
    while True:
        msg = input(">>>>>:") # 可以发送空消息,UDP是面向消息的协议所以不存在粘包现象
        # 发送消息,消息+ addr地址
        client.sendto(msg.encode("utf8"), ("127.0.0.1", 9900))
        data, server_addr = client.recvfrom(1024)
        print(data)
    client.close()	
    

    6.TCP VS UDP区别

    tcp基于链接通信

    基于链接,则需要listen(backlog),指定连接池的大小

    基于链接,必须先运行的服务端,然后客户端发起链接请求

    对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)

    对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)

    udp无链接

    无链接,因而无需listen(backlog),更加没有什么连接池之说了

    无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失

    recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错

    只有sendinto发送数据没有recvfrom收数据,数据丢失

  • 相关阅读:
    Android中内容观察者的使用---- ContentObserver类详解 (转)
    Android应用中使用及实现系统“分享”接口
    logcat的调试 比较有用的几个命令
    ASP.NET AJAX入门系列(10):Timer控件简单使用
    ASP.NET AJAX入门系列(9):在母版页中使用UpdatePanel
    ASP.NET AJAX入门系列(8):自定义异常处理
    ASP.NET AJAX入门系列(7):使用客户端脚本对UpdateProgress编程
    ASP.NET AJAX入门系列(6):UpdateProgress控件简单介绍
    ASP.NET AJAX入门系列(5):使用UpdatePanel控件(二) UpdatePanel
    ASP.NET AJAX入门系列(4):使用UpdatePanel控件(一)
  • 原文地址:https://www.cnblogs.com/zjcode/p/8857917.html
Copyright © 2020-2023  润新知