思想:
1. 客户端 向服务端 发起连接
2. 服务端 接到请求,双方建立连接
3. 客户端 向 服务端发消息
4. 服务端 应答客户端
5. 服务端 与客户端循环读写操作
6. 操作完成之后客户端发起关闭请求
一、客户端与服务端建立连接
客户端
import socket
def main():
# 创建tcp客户端套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服务端程序建立连接,
tcp_client_socket.connect(("localhost", 9999))
# 代码执行到此,说明连接建立成功
# 准备发送的数据
send_data = "你好服务端,我是你永远得不到的爸爸!".encode()
# 发送数据
tcp_client_socket.send(send_data)
# 接收数据, 这次接收的数据最大字节数是1024
recv_data = tcp_client_socket.recv(1024)
# 返回的直接是服务端程序发送的二进制数据,对数据进行解码
recv_content = recv_data.decode("utf-8")
print("接收服务端的数据为:", recv_content)
# 关闭套接字
tcp_client_socket.close()
if __name__ == '__main__':
main()
服务端
import socket
def main():
# 以TCP协议初始化 socket
#
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口和ip(这里我使用的是本机ip)
# 注意:端口号和ip需要放在一个元组里
tcp_server_socket.bind(('localhost', 9999))
# 开启端口监听,设置监听数量
tcp_server_socket.listen(128)
# 设置阻塞,等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 专门和客户端通信的套接字: service_client_socket
# 客户端的ip地址和端口号: ip_port
server_client_socket, ip_port = tcp_server_socket.accept()
# 打印客户端ip和port,验证连接建立成功
print("客户端的ip地址和端口号:", ip_port)
# 接收客户端发送的数据, 这次接收数据的最大字节数是1024
recv_data = server_client_socket.recv(1024)
# 对二进制数据进行解码
recv_content = recv_data.decode("utf-8")
print("接收客户端的数据为:", recv_content)
# 准备发送的数据
send_data = "你好,我是服务端!".encode()
# 发送数据给客户端
server_client_socket.send(send_data)
# 关闭服务与客户端的套接字, 终止和客户端通信的服务
server_client_socket.close()
# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
tcp_server_socket.close()
if __name__ == '__main__':
main()
二、客户端与服务端单任务聊天
客户端
def main():
# 创建tcp客户端套接字
tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服务端程序建立连接,
tcp_client_socket.connect(("localhost", 9999))
# 循环和服务端聊天,输入'bye'表示我(客户端)要结束本次聊天
while True:
# 准备发送的数据
send_data = input("我说:")
if send_data == 'bye':
# 关闭套接字
tcp_client_socket.close()
break
send_data = send_data.encode()
# 发送数据
tcp_client_socket.send(send_data)
# 接收数据, 这次接收的数据最大字节数是1024
recv_data = tcp_client_socket.recv(1024)
# 返回的直接是服务端程序发送的二进制数据,对数据进行解码
recv_content = recv_data.decode("utf-8")
print("服务端说:", recv_content)
if __name__ == '__main__':
main()
服务端
def main():
# 以TCP协议初始化 socket
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用,让程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口和ip(这里我使用的是本机ip)
# 注意:端口号和ip需要放在一个元组里
tcp_server_socket.bind(('localhost', 9999))
# 开启端口监听,设置监听数量
tcp_server_socket.listen(128)
# 设置阻塞,等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
# 专门和客户端通信的套接字: service_client_socket
# 客户端的ip地址和端口号: ip_port
server_client_socket, ip_port = tcp_server_socket.accept()
# 打印客户端ip和port,验证连接建立成功
print("客户端的ip地址和端口号:", ip_port)
# 循环接收客户端发来的消息
while True:
# 接收客户端发送的数据, 这次接收数据的最大字节数是1024
recv_data = server_client_socket.recv(1024)
# 当服务端关闭连接的时候会向服务端发一个空二进制编码
# 判断客户端是否要断开连接
if recv_data == ''.encode():
print("结束把本次聊天")
# 关闭服务与客户端的套接字, 终止和客户端通信的服务
server_client_socket.close()
break
# 对二进制数据进行解码
recv_content = recv_data.decode("utf-8")
print("客户端说:", recv_content)
# 准备发送的数据
send_data = input("我说:")
send_data = send_data.encode()
# 发送数据给客户端
server_client_socket.send(send_data)
# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
tcp_server_socket.close()
if __name__ == '__main__':
main()
三、客户端与服务端多任务聊天
客户端
import socket
class ClientServer():
def __init__(self):
# 创建tcp客户端套接字
self.tcp_client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 和服务端程序建立连接,
self.tcp_client_socket.connect(("localhost", 9999))
# 循环和服务端聊天,输入'bye'表示我(客户端)要结束本次聊天
def run(self):
while True:
# 准备发送的数据
send_data = input("我说:")
if send_data == 'bye':
# 关闭套接字
self.tcp_client_socket.close()
break
send_data = send_data.encode()
# 发送数据
self.tcp_client_socket.send(send_data)
# 接收数据, 这次接收的数据最大字节数是1024
recv_data = self.tcp_client_socket.recv(1024)
# 返回的直接是服务端程序发送的二进制数据,对数据进行解码
recv_content = recv_data.decode("utf-8")
print("服务端说:", recv_content)
if __name__ == '__main__':
client = ClientServer()
client.run()
服务端
import socket
import threading
class TcpServer():
def __init__(self):
# 以TCP协议初始化 socket
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用,让程序退出端口号立即释放
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口和ip(这里我使用的是本机ip)
# 注意:端口号和ip需要放在一个元组里
self.tcp_server_socket.bind(('localhost', 9999))
# 开启端口监听,设置监听数量
self.tcp_server_socket.listen(128)
# 客户端运行方法
def run(self):
# 让客户端进入循环阻塞状态
while True:
# 设置阻塞,等待客户端建立连接的请求, 只有客户端和服务端建立连接成功代码才会解阻塞,代码才能继续往下执行
server_client_socket, ip_port = self.tcp_server_socket.accept()
# 当有客户端连接的时候创建聊天线程
ct = threading.Thread(target=self.chat,args=(server_client_socket, ip_port))
ct.start()
# 关闭服务端的套接字, 终止和客户端提供建立连接请求的服务
# 服务端不能自动关闭
# self.tcp_server_socket.close()
# 聊天方法
def chat(self,server_client_socket,ip_port):
# 打印客户端ip和port,验证连接建立成功
print("新的客户端连接", ip_port)
# 循环接收客户端发来的消息
while True:
# 接收客户端发送的数据, 这次接收数据的最大字节数是1024
recv_data = server_client_socket.recv(1024)
# 当服务端关闭连接的时候会向服务端发一个空二进制编码
# 判断客户端是否要断开连接
if recv_data == ''.encode():
print("结束与客户端%s的聊天"%ip_port[0])
# 关闭服务与客户端的套接字, 终止和客户端通信的服务
server_client_socket.close()
break
# 对二进制数据进行解码
recv_content = recv_data.decode("utf-8")
print("客户端%s说:%s" % (ip_port[0], recv_content))
# 准备发送的数据
send_data = input("我说:")
send_data = send_data.encode()
# 发送数据给客户端
server_client_socket.send(send_data)
if __name__ == '__main__':
tcp_server = TcpServer()
tcp_server.run()
五、客户端与服务端多任务下载文件
客户端
import socket,os,threading,time
class FileClient():
def __init__(self,file_name):
self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# ip和端口号对应你需要访问的服务器
self.client_socket.connect(("192.168.90.92", 8888))
# 发送需要下载的文件名字
self.file_name = file_name
# 保存的地址
self.file_path = "D:/" + self.file_name
def run(self):
self.client_socket.send(self.file_name.encode())
# 接收大小
recv_size = int(self.client_socket.recv(1024).decode())
# 判断文件是否存在
if recv_size == 0:
print("文件不存在")
self.client_socket.close()
else:
# 开一个线程计算进度
t = threading.Thread(target=self.plan, args=(self.file_path, recv_size))
t.start()
# 将接收的二进制数据流写入本地文件
with open(self.file_path, "wb") as f:
while True:
# 从服务端接收的二进制数据
x = self.client_socket.recv(1024)
# 判断是否接收完
if x:
f.write(x)
else:
self.client_socket.close()
break
# 下载进度计算方法
def plan(self, file_path, recv_size):
while True:
# 回去本地该文件
if os.path.exists(file_path):
# 获取下载的文件目前的大小
now_size = os.path.getsize(file_path)
# 进度计算用 目前的大小 /文件的总大小
jindu = (now_size / recv_size) * 100
print("\r下载进度:%.2f%%" % jindu, end="")
# 判断文件是否现在完成
if int(jindu) == 100:
print("\n下载完成,文件路径为:%s"%self.file_path)
break
time.sleep(0.01)
if __name__ == '__main__':
file_name = input("请输入你要下载的文件名:")
file_client = FileClient(file_name)
file_client.run()
服务端
import socket, os , threading
class FileServer():
def __init__(self):
# 建立连接
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 端口复用
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定ip和端口号
self.tcp_server_socket.bind(("", 8888))
# 设置监听
self.tcp_server_socket.listen(128)
def run(self):
while True:
client_socket, ip_port = self.tcp_server_socket.accept()
t = threading.Thread(target=self.real, args=(ip_port, client_socket))
t.start()
def real(self,ip_port, client_socket):
print(ip_port, "客户端连接")
# 接收信息,接收的是要下载的路径
recv_path = client_socket.recv(1024).decode()
print("%s客户端要下载的文件是: %s" % (ip_port, recv_path))
# 判断文件是否存在
if recv_path in os.listdir():
# 发送文件大小给客户端
picture_size = str(os.path.getsize(recv_path))
client_socket.send(picture_size.encode())
# 以二进制的方式循环读取文件
with open(recv_path, 'rb') as f:
while True:
x = f.read(1024)
# 判断文件是否读取完毕
if x:
# 发送本次读取的二进制流
client_socket.send(x)
else:
client_socket.close()
break
else:
# 文件不存在则发送“0”告诉客户端
client_socket.send("0".encode())
sev = client_socket.recv(1024).decode()
client_socket.close()
if __name__ == '__main__':
file_server = FileServer()
file_server.run()