python网络编程之socket

目录

socket

创建客户端 socket 对象

创建服务端 socket 对象 

 多线程服务器端

socket之send和recv原理剖析

1. 认识TCP socket的发送和接收缓冲区

2. send原理剖析

3. recv原理剖析

4. send和recv原理剖析图


socket

为了保证数据的完整性和可靠性我们使用 tcp 传输协议进行数据的传输,udp是不管有没有建立连接成功都会进行发送,如飞Q,邮件,为了能够找到对应设备我们需要使用 ip 地址,为了区别某个端口的应用程序接收数据我们需要使用端口号,那么通信数据是如何完成传输的呢?

使用 socket 来完成

负责进程之间的网络数据传输,只要跟网络相关的应用程序或者软件都使用到了 socket

TCP 网络应用程序开发分为:

  • TCP 客户端程序开发
  • TCP 服务端程序开发

客户端:

步骤说明:

  1. 创建客户端套接字对象
  2. 和服务端套接字建立连接
  3. 发送数据
  4. 接收数据
  5. 关闭客户端套接字

服务器

步骤说明:

  1. 创建服务端端套接字对象
  2. 绑定端口号
  3. 设置监听
  4. 等待接受客户端的连接请求
  5. 接收数据
  6. 发送数据
  7. 关闭套接字

导入 socket 模块 import socket

创建客户端 socket 对象

 socket.socket(AddressFamily, Type)

参数说明:

  • AddressFamily 表示IP地址类型, 分为TPv4和IPv6
  • Type 表示传输协议类型

方法说明:

  • connect((host, port)) 表示和服务端套接字建立连接, host是服务器ip地址,port是应用程序的端口号
  • send(data) 表示发送数据,data是二进制数据
  • recv(buffersize) 表示接收数据, buffersize是每次接收数据的长度

代码分析:

socket是遵循tcp/ip协议的,创建socket对象传入两参数,

并与服务器去建立连接 传入服务器ip port 元组

发送二进制数据,并编码

接收二进制数据并解码,

最后关闭socket,

一句话:socket对象与服务器连接后发送数据 或接收数据

import socket

if __name__=='__main__':
    # 创建socket对象,并传入tcp/ip,
    # socket.AF_INET ipv4
    # socket.SOCK_STREAM tcp
    tcp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_client_socket.connect(('192.168.0.112',8989))
    # 发送二进制数据
    tcp_client_socket.send('你好'.encode('utf8'))
    # 接收二进制数据并解码
    res=tcp_client_socket.recv(1024).decode('utf8')
    print('收到服务器的消息:',res)
    tcp_client_socket.close()

创建服务端 socket 对象 

  • bind((host, port)) 表示绑定端口号, host 是 ip 地址,port 是端口号,ip 地址一般不指定,表示本机的任何一个ip地址都可以。
  • listen (backlog) 表示设置监听,backlog参数表示最大等待建立连接的个数。
  • accept() 表示等待接受客户端的连接请求
  • send(data) 表示发送数据,data 是二进制数据
  • recv(buffersize) 表示接收数据, buffersize 是每次接收数据的长度

 代码分析创建socket对象,

程序结束立即释放端口号

绑定端口号设置监听客户端连接的个数

等待客户端的连接,之后接收客户端的请求

import socket

if __name__=='__main__':
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 程序退出端口号立即释放
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    # 绑定端口号
    tcp_server_socket.bind(('',8989))
    # 监听最多128个连接数
    tcp_server_socket.listen(128)
    # 等待客户端的连接请求,只有连接后程序才会往下执行
    # 建立连接后获取的2个参数   1和客户端通信的套接字,2客户端端口号
    # 建立连接后TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字
    service_client_socket,ip_port=tcp_server_socket.accept()
    print(ip_port)
    # 接收客户端消息
    recv_data=service_client_socket.recv(1024)
    #服务端可以通过返回数据的长度来判断客户端是否已经下线
    recv_data_length=len(recv_data)
    recv_content=recv_data.decode('utf8')
    print(recv_content)
    send_data = "ok, 问题正在处理中...".encode("utf8")
    # 发送数据给客户端
    service_client_socket.send(send_data)
    # 关闭服务与客户端的套接字, 终止和客户端通信的服务
    service_client_socket.close()
    tcp_server_socket.close()

说明:

当客户端和服务端建立连接后,服务端程序退出后端口号不会立即释放,需要等待大概1-2分钟。

解决办法有两种:

  1. 更换服务端端口号
  2. 设置端口号复用(推荐大家使用),也就是说让服务端程序退出后端口号立即释放。

设置端口号复用的代码如下:

# 参数1: 表示当前套接字
# 参数2: 设置端口号复用选项
# 参数3: 设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)

 多线程服务器端

import socket
import threading
import time


def handle_client_request(service_client_socket, ip_port):
    # 循环处理请求
    while True:
        time.sleep(5)
        data=service_client_socket.recv(1024)
        # 若存在数据,即消息长度不是0 则打印 长度为0则客户端下线
        if data:
            print('客户端发来的消息',data.decode('utf8'),'端口号',ip_port)
            # 特别注意:客户端服务端一旦连接,双方的收发必须一一对应,有发就有收,毕竟是基于TCP协议的,
            # 不然就成UDP协议的了
            service_client_socket.send("ok,问题正在处理中...".encode("utf8"))
        else:
            print('客户端下线了')
            break
    service_client_socket.close()

if __name__=='__main__':
    tcp_server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
    tcp_server_socket.bind(('',8989))
    tcp_server_socket.listen(128)
    # 准备工作做完后循环执行
    while True:
        #  等待接收客户端的连接请求
        service_client_socket,ip_port=tcp_server_socket.accept()
        # 一旦走到这说明建立连接成功,连接后开启子线程完成多任务
        #传入处理客户端请求的新socket和端口号
        sub_t=threading.Thread(target=handle_client_request,args=(service_client_socket,ip_port),daemon=True)
        # 此时需要开启守护主线程,因为建立连接后才执行到子线程处理,此时若再子线程工作的时候客户端断开连接了
        # 主线程无法正常断开连接
        #分析:子线程执行需要5秒  再5秒内被客户端强制退出 导致子线程未执行完主线程就挂掉了
        #Error 10054] 远程主机强迫关闭了一个现有的连接,要求子线程跟随主线程的生命周期
        sub_t.start()
        # 这里不用关闭服务器socket  让其一直可以接收客户端发消息

socket之send和recv原理剖析

1. 认识TCP socket的发送和接收缓冲区

当创建一个TCP socket对象的时候会有一个发送缓冲区和一个接收缓冲区这个发送和接收缓冲区指的就是内存中的一片空间。

2. send原理剖析

send是不是直接把数据发给服务端?

不是,要想发数据,必须得通过网卡发送数据,应用程序是无法直接通过网卡发送数据的,它需要调用操作系统接口,也就是说,应用程序把发送的数据先写入到发送缓冲区(内存中的一片空间),再由操作系统控制网卡把发送缓冲区的数据发送给服务端网卡 

3. recv原理剖析

recv是不是直接从客户端接收数据?

不是,应用软件是无法直接通过网卡接收数据的,它需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区(内存中的一片空间),应用程序再从接收缓存区获取客户端发送的数据

4. send和recv原理剖析图

 

说明:

  • 发送数据是发送到发送缓冲区
  • 接收数据是从接收缓冲区 获取
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值