TCP网络编程_socket
直接干实例1:
1V1 聊天程序(TCP)
需求: 使用TCP 显示两个人的1V1即时聊天服务
客户端
# 实现利用TCP协议的1V1聊天程序
from socket import *
def V1():
# 创建对象
client_1 = socket(AF_INET, SOCK_STREAM)
# 准备地址
address = ('192.168.2.104', 7877)
# 发送地址
client_1.connect(address)
while True:
context = input('请留言:')
if context == "exit":
print('已结束聊天!')
break
try:
client_1.send(context.encode('utf-8'))
except:
print('发送失败!连接已中断')
break
# 接收数据
server_data = client_1.recv(1024)
while True:
if server_data:
print(f'来自回复:{server_data.decode("utf-8")}')
break
else:
break
def main():
V1()
if __name__ == '__main__':
main()
服务端
# 实现利用TCP协议的1V1聊天程序
from socket import *
def V2():
# 创建对象
client_2 = socket(AF_INET, SOCK_STREAM)
# 准备地址
address = ('', 7877)
# 发送地址
client_2.bind(address)
client_2.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 最大连接数
client_2.listen(2)
news_client, address_client = client_2.accept()
while True:
try:
# 获取内容
raw_data = news_client.recv(1024).decode('utf-8')
except:
print('连接已中断!')
break
if raw_data.split():
print(f'来自: {address_client[0]} 留言:{raw_data}')
context = input('回复内容:')
if context:
news_client.send(context.encode('utf-8'))
else:
print('聊天已结束!')
break
else:
print('聊天已结束!')
break
def main():
V2()
if __name__ == '__main__':
main()
结果: 效果初步实现,但不足的是只能一句一句的回复,同时间只能有一方说话 ( 例: 对讲机 )
实例二
自定义FTP协议功能
需求: 使用SOCKET实现文件的上传,下载,查看功能
FTP客户端
# 设定 L:读 G: 下载 P: 上传
from socket import *
import pickle
def upload(data):
# 创建对象
upload_client = socket(AF_INET, SOCK_STREAM)
# 超时管理
upload_client.settimeout(1)
# 地址
upload_address = ('172.16.17.197', 7788) # 连接服务器
try:
upload_client.connect(upload_address)
except:
print('连接服务端失败!')
# 打开文件
with open(f'{data[2:]}', 'rb') as f:
# 读取文件
file_data = f.read(1024)
# 发送文件头
upload_client.send((data + '|@_@|').encode('utf-8'))
res = 0
while file_data:
# 发送指令
res += upload_client.send(file_data)
file_data = f.read(1024)
upload_client.close()
print(f'上传{res} 个字节完成!')
return True
def download(data):
# 创建对象
download_client = socket(AF_INET, SOCK_STREAM)
# 超时管理
download_client.settimeout(1)
# 地址
download_address = ('172.16.17.197', 7788)
# 连接服务器
try:
download_client.connect(download_address)
# 发送指令
download_client.send(data.encode('utf-8'))
except:
print('连接服务端错误,发送失败!')
# 接收响应结果
raw_data = download_client.recv(1024)
# 打开文件
with open(f'{data[2:]}_bak', 'wb') as f:
# 写入文件
while raw_data:
f.write(raw_data)
# print(raw_data)
raw_data = download_client.recv(1024)
f.close()
download_client.close()
print('下载完成!')
return True
def show(data):
# 创建对象
show_client = socket(AF_INET, SOCK_STREAM)
# 地址
show_address = ('172.16.17.197', 7788)
# 超时管理
show_client.settimeout(1)
# 连接服务器
try:
show_client.connect(show_address)
# 发送指令
show_client.send(data.encode('utf-8'))
except:
print('连接服务端错误,发送失败!')
# 接收响应结果
try:
raw_data = show_client.recv(1024)
except:
print('服务端未响应')
else:
print('结果:', pickle.loads(raw_data))
show_client.close()
def main():
while True:
inputData = input('输入命令:').strip() # 去除空格
if inputData[0].upper() == "L":
show(inputData)
elif inputData[0].upper() == "G":
return_res = download(inputData)
if return_res:
# 下载完成
break
else:
print('下载失败!')
break
elif inputData[0].upper() == "P":
# 是否上传完成
res = upload(inputData)
if res:
# 上传完成
break
else:
print('上传失败!')
break
else:
print('命令错误!')
break
if __name__ == '__main__':
main()
FTP服务端
import os
from socket import *
import pickle
# 查看
def show(news_socket):
# 查询当前目录下文件
files = os.listdir(os.getcwd())
# dumps 序列化
news_socket.send(pickle.dumps(files))
# 关闭
news_socket.close()
# 下载
def download(news_socket, raw_data):
# 获取文件名
with open(f'{raw_data[2:]}', 'rb') as f:
# 读取文件
file_data = f.read(1024)
send_size = 0
while file_data:
# 发送字节码
send_size += news_socket.send(file_data)
file_data = f.read(1024)
print(f'已下载:{send_size} 个字节')
news_socket.close()
def upload(file_data, news_socket):
# 解出文件名
file_name = file_data.split(b'|@_@|')[0]
file_name = file_name.split(b':')[1]
with open(file_name.decode('utf-8') + '_bak', 'ab') as f:
# 接收数据
files_data = file_data.split(b'|@_@|')[1]
raw_data = news_socket.recv(1024)
if files_data:
f.write(files_data)
while raw_data:
# 写入
f.write(raw_data)
raw_data = news_socket.recv(1024)
news_socket.close()
def main():
# 创建对象
ftp_server = socket(AF_INET, SOCK_STREAM)
# 绑定端口
ftp_server.bind(('', 7788))
# 同时最大连接数
ftp_server.listen(5)
while True:
try:
# 创建新
news_socket, client_info = ftp_server.accept()
# 获取指令
# 获取原始数据 指令
raw_data = news_socket.recv(1024)
if raw_data[0:1].upper().decode('utf-8') == "L":
# 查看
show(news_socket)
elif raw_data[0:1].upper().decode('utf-8') == "G":
# 下载
download(news_socket, raw_data.decode('utf-8'))
elif raw_data[0:1].upper().decode('utf-8') == "P":
upload(raw_data, news_socket)
else:
# 未检测到内容输入,退出操作
ftp_server.close()
break
except:
print('参数异常!')
break
if __name__ == '__main__':
main()
序列化与反序列化
通过pickle的dumps方法把任何数据转化成可以网络上传输的bytes类型,通常用于服务端序列化,客户端反序列化
import pickle
var_a = {'a':'str', 'c': True, 'e': 10, 'b': 11.1, 'd': None, 'f': [1, 2, 3], 'g':(4, 5, 6)}
# 序列化
>>> var_b = pickle.dumps(var_a)
>>> var_b
b'\x80\x03}q\x00(X\x01\x00\x00\x00eq\x01K\nX\x01\x00\x00\x00aq\x02X\x03\x00\x00\x00strq\x03X\x01\x00\x00\x00fq\x04]q\x05(K\x01K\x02K\x03eX\x01\x00\x00\x00gq\x06K\x04K\x05K\x06\x87q\x07X\x01\x00\x00\x00bq\x08G@&333333X\x01\x00\x00\x00cq\t\x88X\x01\x00\x00\x00dq\nNu.'
# 反序列化
>>> var_c = pickle.loads(var_b)
>>> var_c
{'e': 10, 'a': 'str', 'f': [1, 2, 3], 'g': (4, 5, 6), 'b': 11.1, 'c': True, 'd': None}
dump()用与保存序列化结果到文件,再称之为持久
load()用于从文件中取内容后坍塌反序列化
持久化:一切内存里的东西写入硬盘后就持久化,开机后能再次看到
TCP三次握手和四次挥手
TCP为什么三次分手
- 第一次握手 客户端向服务端发送一个请求包 当服务端收到这个请求包的时间可以确定服务端的收信功能 和客户端的发信功能
- 第二次握手 服务端向客户端发送一个确认包,并同时发送一个请求包, 客户端可以确认 自己可以收发信,服务端也可以收发信
- 第三次握手 客户端向服务端发送一个确认包 服务端可以确认 自己的发信功能和客户端的收信功能,结合第一次握手的信息已经确认双方收发信功能没有问题
TCP为什么要四次挥手
- 客户端发送一个分手包
- 服务端收到后立马确认
- 把手头剩余的事情做完后再次发送分手包给客户端
- 客户端收到后再次发送确认包等待2MSL时间后退出,服务端收到确认包后结束连接,如果在没有收到会再次发送分手包
什么是2MSL
MSL是客户端到服务端一次通信的最长时间,如果在这个时间内没有送达,认为丢包了
一来一回最长时间就是2MSL,所以任何一方在发出信息后,如果2MSL内没有收到回复,就认为丢包
通常在四次握手最后一次,客户端发出最后确认分手包后,服务端在收到这个包后会断开连接,无法再发送消息
所以客户端只有等待这么长时间,如果在这个时间段内没有再次收到新的分手包,就认为服务端已经下线,我也是可以离开