python创建TCP Client

1. TCP 客户端与服务端通信


创建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()

先启动服务端,然后在另一个终端里启动客户端,客户端与服务端完成一次通信,随后都关闭

2. 客户端代码解读

客户端代码似乎和服务端代码很相似,对比一下有四处不同。

客户端代码没有bind

客户端代码没有listen

客户端代码没有accept

客户端需要connect

2.1 connect

服务端不需要去connect谁,它只需要等待客户端来连接它就可以了,所以客户端必须bind,指定监听的IP和端口,因此,在socket应用程序中,必须先启动服务端,启动后服务端进行listen,客户端发起connect,服务端accept

2.2 bind

客户端其实也能进行bind操作,指明用哪个端口与服务端通信,但通常来说不会这么做,原因很简单,服务端一般来说要一直提供服务,轻易不会停下来,而客户端却可能经常停下来,想想上一篇讲的time_wait,再有,如果客户端的程序是多线程的,多个线程里总不能同时去bind同一个端口吧。所以不bind是最好的,让操作系统来随机分配端口

2.3 send

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

2.4 recv

关于recv,虽然参数是1024,但这不代表你就能接收到这么多的数据,这个参数的意思是我想接收1024个字节的数据,但socket协议最终给你的可能比你要求的少,想想也合理,因为TCP是不维护数据边界的,一次给你多少不影响你的分析,但绝不会超过你所要求的,如果缓冲区里有数据了,也没必要非得等到缓冲区的数据长度达到1024再给你。如果recv得到的数据长度是0,那就表示对方已经断开了连接

2.5 分包与粘包

一个tcp包最多可以装1460个字节的数据,所以如果你要发送的数据长度是2000个字节,那么这些数据最少要分成两份,也就是两个TCP包发过去,如果你频繁的发送长度只有10个字节的数据,那么为了不浪费网络带宽,这些大量的长度只有10的数据会被装进同一个TCP包中一起发送过去,这就是TCP的分包与粘包。这个对于接收方来说就是个麻烦,由于TCP自己不维护数据边界,因此应用程序本身必须对此进行处理,以便应对一段完整数据被分多个包发送(分包)和多个小段数据被一起发送(粘包)的情况,下一篇将给出一个非常简单的解决方案