Python3网络编程篇之简单的socket通信

导入

import socket

Server端

创建基于网络的TCP

socket.socket()

# 基于网络的TCP
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

端口占用问题

端口占用会抛出 OSError

起因

这是因为服务端仍然存在四次挥手的time_wait状态在占用地址

解决

sk.setsockopt()

sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

绑定ip和port

ip:port,用来唯一标识一台主机上的一个应用程序
注意点:以元组形式传入
sk.bind((ip, port))
以下四种形式都可以:

  • 绑定回环地址
    • sk.bind(('localhost', 8080))
    • sk.bind(('', 8080))
    • sk.bind(('127.0.0.1', 8080))
  • 绑定本机地址
    • sk.bind(('192.168.1.103', 8080))
      如果绑定了本机地址,那么就表示只要能ping通这个地址的主机,就能够访问该server

监听

sk.listen(backlog)

sk.listen(5)

标识最大等待连接数。注意:是客户端的等待连接,正在通信的不算。

换句话说:排队的人数(就是那个n) + 正在就餐的人数(服务器正在处理的socket连接数) = 允许接待的总人数(socket允许的最大连接数)

深入理解
        accept()方法一次只能接受一个Client的连接申请,但Client是多个的,于是socket
设计了一个队列来存储Client的连接申请,从而accept()方法就从这个队列中提取首位
成员处理即可。
所以,参数backlog指的就是这个队列的最大值,也就是同时受理连接申请的最大值,如果满了,就需要Client重新Connect。
关于backlog该设置多少,从Skynet得到的参考为32

具体可以参考以下两篇博文:

接收连接

sk.accept()
accept(),返回一个元祖

  • [0]是一个与Client通信的双向连接
  • [1]Client地址
conn, addr = sk.accept()

收与发数据

收数据

conn.recv()
recv两个阶段

  1. 等待数据到达内核态
  2. 从内核态copy数据到用户态
data = conn.recv(1024)  # 参数1024表示,最多接收1024个字节
print(data.decode(encoding='utf-8'))

发数据

conn.send()
返回值:发送数据的字节数

conn.send(bytes('received', encoding='utf-8'))

说明
send一个阶段

  1. copy数据到内核态

使用send()进行发送的时候,Python将内容传递给系统底层的send接口,也就是说,Python并不知道这次调用是否会全部发送完成,比如MTU是1500,但是此次发送的内容是2000,那么除了包头等等其他信息占用,发送的量可能在1000左右,还有1000未发送。

但是,send()不会继续发送剩下的包,因为它只会发送一次,发送成功之后会返回此次发送的字节数,如上例,会返回数字1000给用户,然后就结束了

如果需要将剩下的1000发送完毕,需要用户自行获取返回结果,然后将内容剩下的部分继续调用send()进行发送,比如:

sended = 0
while 1:
    sended = conn.send(result[sended:])
    if not sended:
        break

conn.sendall()
sendall()是对send()的包装,完成了用户需要手动完成的部分,它会自动判断每次发送的内容量,然后从总内容中删除已发送的部分,将剩下的继续传给send()进行发送。

什么时候使用send()什么时候使用sendall()

一般情况下,我们都应该使用sendall(),除非自己弄懂了他们的原理,并且有必要在每次包发送之后进行一些必要的处理,不然我们都不需要去使用send(),而应该使用已经包装好的sendall()


借鉴源
https://blog.csdn.net/jing16337305/article/details/79856116
https://blog.csdn.net/pdmwg/article/details/39226719

断开与client的连接

conn.close()

conn.close()

断开socket的连接

sk.close()

sk.close()

说明

Server端有两个socket套接字

  1. conn
  2. sk

基于TCPsocket,得先启动Server

Server可以先收也可以先发,Client也一样

TCP是面向连接的、安全的、可靠的、面向流的无消息保护边界

server.py整体代码

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""简单的socket通信

Server端
"""

import socket

# 基于网络的TCP
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 端口占用会抛出 OSError
# 端口占用的起因
# 这是因为服务端仍然存在四次挥手的time_wait状态在占用地址

# 解决端口占用问题
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

# 绑定ip和port,用来唯一标识一台主机上的一个应用程序
# 注意点:以元组形式传入
# 以下四种形式都可以:
# 绑定回环地址
sk.bind(('localhost', 8080))
# sk.bind(('', 8080))
# sk.bind(('127.0.0.1', 8080))
# 绑定本机地址
# sk.bind(('192.168.1.103', 8080))
# 如果绑定了本机地址,那么就表示只要能ping通这个地址的主机,就能够访问该server

# 监听
# listen(backlog)
# 标识最大等待连接数。注意:是客户端的等待连接,正在通信的不算
# 换句话说:排队的人数(就是那个n) + 正在就餐的人数(服务器正在处理的socket连接数) = 允许接待的总人数(socket允许的最大连接数)
# 深入理解:
# accept()方法一次只能接受一个Client的连接申请,但Client是多个的,于是socket
# 设计了一个队列来存储Client的连接申请,从而accept()方法就从这个队列中提取首位
# 成员处理即可。
# 所以,参数backlog指的就是这个队列的最大值,也就是同时受理连接申请的最大值,
# 如果满了,就需要Client重新Connect。
# 关于backlog该设置多少,从Skynet得到的参考为32
# 具体可以参考以下两篇博文:
# https://blog.csdn.net/u010275932/article/details/76081735
# https://www.cnblogs.com/gaohuayan/p/11139397.html
sk.listen(5)

# 接收连接
# accept(),返回一个元祖,[0]是一个与Client通信的双向连接;[1]是Client的地址
conn, addr = sk.accept()

# 收、发数据
# 收
# recv两个阶段
# 1. 等待数据到达内核态
# 2. 从内核态copy数据到用户态
data = conn.recv(1024)  # 参数1024表示,最多接收1024个字节
print(data.decode(encoding='utf-8'))

# 发
# 返回值:发送数据的字节数
# send一个阶段
# 1. copy数据到内核态
conn.send(bytes('received', encoding='utf-8'))

# 断开与Client的连接
conn.close()

# 断开socket的连接
sk.close()

# 说明:
# Server端有两个socket套接字
# 1. conn
# 2. sk

# 基于TCP的socket,得先启动Server
# Server可以先收也可以先发,Client也一样

# TCP是面向连接的、安全的、可靠的、面向流的无消息保护边界

Client端

创建基于网络的TCP

socket.socket()

# 基于网络的TCP
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

发起请求连接

ip:port发起连接请求
客户端connect的时候ip不能写空,跟服务器端稍微有点区别
sk.connect((ip, port))

sk.connect(('localhost', 8080))

当目标主机不存在时,connect抛出ConnectionRefusedError异常
connect()函数的扩展版本connect_ex(),出错时返回出错码,而不是抛出异常;连接正常时,返回0

print(sk.connect_ex(('localhost', 8080)))

收与发数据

发数据

conn.send()

sk.send(bytes('我要连你...', encoding='utf-8'))

收数据

conn.recv()

data = sk.recv(1024)
print(data.decode(encoding='utf-8'))

关闭socket连接

sk.close()

sk.close()

client.py整体代码

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""简单的socket通信

Client端
"""

import socket

# 基于网络的TCP
sk = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)

# 向ip:port发起连接请求
sk.connect(('localhost', 8080))

# 发
sk.send(bytes('我要连你...', encoding='utf-8'))

# 收
data = sk.recv(1024)
print(data.decode(encoding='utf-8'))

# 关闭socket连接
sk.close()

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值