创建TCP 客户端与 TCP server通信
import socket
import time
host = '127.0.0.1'
port = 8081
addr = (host, port)
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 连接server
client.connect(addr)
# 向server发送数据
client.send(b'I am client')
# 接收server返回的数据
revcdata = client.recv(1024)
# 收到的数据都是bytes类型
print(revcdata.decode(encoding='utf-8'))
time.sleep(1)
client.close()
为了配合客户端测试,同时编写服务端代码
import socket
# 指定协议
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 让端口可以重复使用
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定ip和端口
server.bind(('0.0.0.0', 8081))
# 监听
server.listen(1)
# 等待连接
clientsocket, address = server.accept()
# 接收消息
data = clientsocket.recv(1024)
clientsocket.send('我已经收到'.encode(encoding='utf-8'))
clientsocket.close()
server.close()
先启动服务端,然后在另一个终端里启动客户端,客户端与服务端完成一次通信,随后都关闭
客户端代码似乎和服务端代码很相似,对比一下有四处不同。
服务端不需要去connect谁,它只需要等待客户端来连接它就可以了,所以客户端必须bind,指定监听的IP和端口,因此,在socket应用程序中,必须先启动服务端,启动后服务端进行listen,客户端发起connect,服务端accept
客户端其实也能进行bind操作,指明用哪个端口与服务端通信,但通常来说不会这么做,原因很简单,服务端一般来说要一直提供服务,轻易不会停下来,而客户端却可能经常停下来,想想上一篇讲的time_wait,再有,如果客户端的程序是多线程的,多个线程里总不能同时去bind同一个端口吧。所以不bind是最好的,让操作系统来随机分配端口
send方法用于发送数据,数据类型必须是bytes类型,字符串与bytes类型之间的相互转换可以参考bytes 字节串
, send函数执行完了,不代表数据已经被发送出去了,函数返回,只是表示这些数据已经被放入到发送缓冲区了,至于何时被发送到网络上,由socket协议自己来决定。
send方法的返回值是本次发送数据的长度,假设你send的数据的长度是1000,send函数执行完了,其返回值可能小于1000,比如返回值是900,这就意味着只有前900个字节的数据被放入到发送缓冲区了,因此在send时,你必须检查send函数的返回值并和你要发送的数据长度做对比,遇到我刚才说的情况,你只好重新发送失败的那部分,可靠的写法类似这样
data = b'I am client'
sendlen = 0
while sendlen < len(data):
successlen = client.send(data[sendlen:])
sendlen += successlen
关于recv,虽然参数是1024,但这不代表你就能接收到这么多的数据,这个参数的意思是我想接收1024个字节的数据,但socket协议最终给你的可能比你要求的少,想想也合理,因为TCP是不维护数据边界的,一次给你多少不影响你的分析,但绝不会超过你所要求的,如果缓冲区里有数据了,也没必要非得等到缓冲区的数据长度达到1024再给你。如果recv得到的数据长度是0,那就表示对方已经断开了连接
一个tcp包最多可以装1460个字节的数据,所以如果你要发送的数据长度是2000个字节,那么这些数据最少要分成两份,也就是两个TCP包发过去,如果你频繁的发送长度只有10个字节的数据,那么为了不浪费网络带宽,这些大量的长度只有10的数据会被装进同一个TCP包中一起发送过去,这就是TCP的分包与粘包。这个对于接收方来说就是个麻烦,由于TCP自己不维护数据边界,因此应用程序本身必须对此进行处理,以便应对一段完整数据被分多个包发送(分包)和多个小段数据被一起发送(粘包)的情况,下一篇将给出一个非常简单的解决方案
QQ交流群: 211426309