day28 8_7 网络编程之tcp协议
一。socket模块
socket模块就是用来网络搭建的模块,socket也叫套接字。
创建网络连接,需要使用两个模块进行模拟,一个作为server服务器端,一个作为client客户端。
在服务器端,需要先申明一个socket,再使用bind等待接入,需要传入IP地址和端口号,这里注意,这两个需要放在一个元组里。
再调用server.listen输入接入的连接池大小,也就是最大可以接受多少客户端的等待。
服务器端:
import socket server = socket.socket()
server.bind(('127.0.0.1',)) #127.0.0.1是本机回还地址
server.listen() #连接池 conn,addr = server.accept()
print(conn)
res = conn.recv()
print(res)
conn.send(b'hello client') conn.close()
server.close()
客户端
import socket client = socket.socket()
client.connect(('127.0.0.1',9000)) client.send(b'hello') res = client.recv(1024)
print(res )
client.close()
其中服务器端的accept是服务器等待接听的过程,返回 一个socket对象,随后可以调用该对象进行接受数据与发送数据的操作。
注意,客户端,与服务器之间需要send和recv操作一一对应,不能出现同时发送和接受的命令,否则回出现双方都在等待对方发送的数据或者收到数据的反馈。
二。循环通信。
上面只是一个简单的客户端与服务器之间的通信,要想实现循环发送数据给服务器,可以使用while循环:
服务器:
import socket server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5) conn,addr = server.accept()
while True:
res = conn.recv(1024)
conn.send(res.upper())
客户端;
import socket client = socket.socket()
client.connect(('127.0.0.1',9000)) while True:
cmd = input('>>>').encode()
client.send(cmd)
res = client.recv(1024)
print(res)
使用循环把send和recv操作放入就可以实现单客户端反复传入数据给服务器并返回对应的值。
其中,当客户端输入回车时,send会将b‘’传给服务器,服务器没有收到数据就会一直等待,导致该次循环不能结束,所以需要添加判断条件。
针对mac与linux 客户端异常退出之后 服务端不会报错 只会一直收b‘’,所以为了兼容性,也需要添加判断条件。
当一个客户端无故的退出了连接后,服务器会报出错误:
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
想要避免这个错误,要使用try except 对其进行异常处理:
服务器
import socket server = socket.socket()
server.bind(('127.0.0.1',9000))
server.listen(5) conn,addr = server.accept()
while True:
try:
res = conn.recv(1024)
if not res: break
conn.send(res.upper())
except ConnectionResetError:
print('一个连接已断开')
break
客户端:
import socket client = socket.socket()
client.connect(('127.0.0.1',9000)) while True:
cmd = input('>>>').encode()
if not cmd: continue
client.send(cmd)
res = client.recv(1024)
print(res)
三。循环连接:
虽然处理了异常,但是当一个客户端连接断开后,服务器依然不能准备好连接下一个客户端,需要对连接进行循环:
服务器
import socket """
服务端
固定的ip和port
24小时不间断提供服务
"""
server = socket.socket() # 生成一个对象
server.bind(('127.0.0.1',8080)) # 绑定ip和port
server.listen(5) # 半连接池 while True:
conn, addr = server.accept() # 等到别人来 conn就类似于是双向通道
print(addr) # ('127.0.0.1', 51323) 客户端的地址
while True:
try:
data = conn.recv(1024)
print(data) # b'' 针对mac与linux 客户端异常退出之后 服务端不会报错 只会一直收b''
if len(data) == 0:break
conn.send(data.upper())
except ConnectionResetError as e:
print(e)
break
conn.close()
客户端
import socket client = socket.socket()
client.connect(('127.0.0.1',8080)) while True:
msg = input('>>>:').encode('utf-8')
if len(msg) == 0:continue
client.send(msg)
data = client.recv(1024)
print(data)
四。粘包问题:
TCP特点 会将数据量比较小的并且时间间隔比较短的数据 一次性打包发送给对方
所以,必须每次控制好recv的数据大小。
这里使用了struct,struct就是可以将一串很长的数据压缩成4个字节,使用‘i'模式,这样就可以提前知道一个数据多大。
所以先使用struct将所传数据的长度发给客户端,客户端准备一个4字节的接受,接受到数据后根据设置的recv大小循环取值,直到数据结束。
服务器:
import json
import socket
import struct server = socket.socket()
server.bind(('127.0.0.1',8010))
server.listen(5) while True:
conn,addr = server.accept()
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
import subprocess
obj = subprocess.Popen(cmd.decode('utf-8'), shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
res = obj.stdout.read()+obj.stderr.read()
header_dict = {'name':'dicr','len':len(res),'something':'lalala'}
json_h=json.dumps(header_dict).encode('utf-8')
json_h_len=struct.pack('i',len(json_h))
conn.send(json_h_len)
conn.send(json_h)
conn.send(res)
except ConnectionResetError:
print('有一个连接断开')
break
客户端:
import socket
import struct
import json client = socket.socket()
client.connect(('127.0.0.1', 8010)) while True:
cmd = input('>>>').encode()
if not cmd: continue
client.send(cmd)
header_dict = client.recv(4)
dict_size = struct.unpack('i', header_dict)[0]
dict = json.loads(client.recv(dict_size).decode('utf-8'))
print(dict)
file_size = dict['len']
real_recv = b''
file_size1 = 0
while file_size1 < file_size:
date = client.recv(1024)
real_recv += date
file_size1 += len(date)
print(real_recv.decode('gbk'))
但是,当所传数据大于struct所能传输的数据时,就会解压不下,所以。
需要先准备一个字典,将长度传给字典保存。
再将字典传给客户端,
客户端收到字典后解析出文件长度,根据长度循环接收数据。
具体版如下:
服务器:
import socket
import subprocess
import struct
import json server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5) while True:
conn, addr = server.accept()
while True:
try:
cmd = conn.recv(1024)
if len(cmd) == 0:break
cmd = cmd.decode('utf-8')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
res = obj.stdout.read() + obj.stderr.read()
d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'}
json_d = json.dumps(d)
# 1.先制作一个字典的报头
header = struct.pack('i',len(json_d))
# 2.发送字典报头
conn.send(header)
# 3.发送字典
conn.send(json_d.encode('utf-8'))
# 4.再发真实数据
conn.send(res)
# conn.send(obj.stdout.read())
# conn.send(obj.stderr.read())
except ConnectionResetError:
break
conn.close()
客户端:
import socket
import struct
import json client = socket.socket()
client.connect(('127.0.0.1',8080)) while True:
msg = input('>>>:').encode('utf-8')
if len(msg) == 0:continue
client.send(msg)
# 1.先接受字典报头
header_dict = client.recv(4)
# 2.解析报头 获取字典的长度
dict_size = struct.unpack('i',header_dict)[0] # 解包的时候一定要加上索引0
# 3.接收字典数据
dict_bytes = client.recv(dict_size)
dict_json = json.loads(dict_bytes.decode('utf-8'))
# 4.从字典中获取信息
print(dict_json)
recv_size = 0
real_data = b''
while recv_size < dict_json.get('file_size'): # real_size = 102400
data = client.recv(1024)
real_data += data
recv_size += len(data)
print(real_data.decode('gbk'))
总结:
服务器:服务端
1.先制作一个发送给客户端的字典
2.制作字典的报头
3.发送字典的报头
4.发送字典
5.再发真实数据
客户端:
1.先接受字典的报头
2.解析拿到字典的数据长度
3.接受字典
4.从字典中获取真实数据的长度
5.接受真实数据
异常处理
当在运行服务器或客户端时,有可能出现这样的错误,即使重启了服务器还是会出现这样的错误,是端口号占用的问题,解决方法如下:
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898)) #把地址绑定到套接字
sk.listen() #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024) #接收客户端信息
print(ret) #打印客户端信息
conn.send(b'hi') #向客户端发送信息
conn.close() #关闭客户端套接字
sk.close() #关闭服务器套接字(可选)
day28 8_7 网络编程之tcp协议的更多相关文章
- 网络编程之tcp协议以及粘包问题
网络编程tcp协议与socket以及单例的补充 一.单例补充 实现单列的几种方式 #方式一:classmethod # class Singleton: # # __instance = None # ...
- 网络编程之TCP协议与UDP协议
了解网络就要了解一些基本的协议今天主要跟大家分享一些关于TCP 协议UDP协议的相关知识 首先介绍一下TCP协议 TCP(Transmission Cintrol Protocol)可靠的.面向连接的 ...
- 网络编程之TCP协议怎么使用?
TCP 通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器会写的数据 表示客户端的类: java.net.Socket;此类实现客户端套接字.套接字是两台机器间通信的端点 套接字:包含了 ...
- java 26 - 7 网络编程之 TCP协议代码优化
上次所写的代码中,客户端和服务器端所进行的数据传输所用的是字节流. 优化: A:这次,为了高效,对这个字节流通过转换流来进行包装,包装成高效字符流. B:这次,传输的数据是通过键盘录入的数据. 服务器 ...
- java 26 - 6 网络编程之 TCP协议 传输思路 以及 代码
TCP传输 Socket和ServerSocket 建立客户端和服务器 建立连接后,通过Socket中的IO流进行数据的传输 关闭socket 同样,客户端与服务器是两个独立的应用程序 TCP协议发送 ...
- python六十九课——网络编程之TCP协议
1.1 概述: TCP协议通过三次握手协议将客户端与服务器端连接,两端使用各自的Socket对象.Socket对象中包含了IO流,供数据传输. 即:TCP协议在客户端与服务器端通过Socket组成了I ...
- java 26 - 9 网络编程之 TCP协议多用户上传文件
TCP实现多用户上传文件: 需要同时给多用户上传文件,这样就得用多线程来实现. 实际上,这样的话,上传的先后顺序和速度就跟客户端的带宽有关:带宽够,就容易抢占到线程的执行权: 首先,创建个线程类:(这 ...
- java 26 - 8 网络编程之 TCP协议的练习
TCP练习: 1.客户端键盘录入,服务器输出文本文件 客户端代码: public class ClientDemo { public static void main(String[] args) t ...
- java 26 - 8 网络编程之 TCP协议上传图片
上次的是上传TXT文件,这次上传的是图片.同样,上传成功需要反馈给客户端. 区别: TXT文件用记事本打开,我们可以看得懂,所以用了缓冲字符流,对通道内的字节流进行包装了. 而图片用记事本打开,我们看 ...
随机推荐
- 【oracle】DATE输出是什么东西
SELECT TO_CHAR(SYSDATE) FROM DUAL;
- 补充: SpringBoot
SpringBoot Spring Boot 约定大于配置 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程. 该框 ...
- nowcoder941B 弹钢琴
题目链接 思路 首先按照音色排个序,顺便离散化一下音高. 用\(h[i]\)表示第\(i\)个键的音高,用\(w[i]\)表示第\(i\)个键的春希度. 朴素\(dp\) \(f[i][j]\)表示前 ...
- 关于XSS攻击
1.XSS XSS(Cross Site Scripting)攻击全称跨站脚本攻击,为了不与CSS(Cascading Style Sheets)混淆,故将跨站脚本攻击缩写为XSS,XSS是一种经常出 ...
- keras.preprocessing.text.Tokenizer
说明:num_words的参数设置,对应着sequences_to_matrix方法返回的arrray的shape[1],用于约束返回数组的第2个维度.对texts_to_sequences(text ...
- HTML连载31-制作一个百度首页
一. 我们制作一个百度首页作为练习,可直接复制该代码保存后缀名为.html来查看 <!DOCTYPE html> <html lang="en"> < ...
- JWT简要说明
什么是JWT? JSON Web Token (JWT) 是一种开放标准 (RFC 7519) 定义了一种用于安全传输的紧凑.自包含(注:或自说明) 的Json结构, 被传输的信息可以通过JWT内容中 ...
- dedecms用runphp功能,写for循环,@me输出不出来
今天在{dede:field name='typeid' runphp='yes'}中写for循环,出现@me输出不了内容,把for循环删掉之后,就可以输出.死了几十万脑细胞,没有解决,后来把循环 f ...
- Centos安装jdk1.8出现-bash: //usr/local/soft/jdk1.8.0_191/bin/javac: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录错误。
1.从来没有这么郁闷,之前安装都是好好的,自从将Centos升级到7.0版本,安装了jdk报了这个错误,也是郁闷的一毛,参考了一下百度的,记录一下.使用java命令还有java -version命令都 ...
- .NET Core on K8S快速入门课程--学习笔记
课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 目录 01-介绍K8s是什么 02-为什么要学习k8s 03-如何学习k8s ...