一。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协议的更多相关文章

  1. 网络编程之tcp协议以及粘包问题

    网络编程tcp协议与socket以及单例的补充 一.单例补充 实现单列的几种方式 #方式一:classmethod # class Singleton: # # __instance = None # ...

  2. 网络编程之TCP协议与UDP协议

    了解网络就要了解一些基本的协议今天主要跟大家分享一些关于TCP 协议UDP协议的相关知识 首先介绍一下TCP协议 TCP(Transmission Cintrol Protocol)可靠的.面向连接的 ...

  3. 网络编程之TCP协议怎么使用?

    TCP 通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器会写的数据 表示客户端的类: java.net.Socket;此类实现客户端套接字.套接字是两台机器间通信的端点 套接字:包含了 ...

  4. java 26 - 7 网络编程之 TCP协议代码优化

    上次所写的代码中,客户端和服务器端所进行的数据传输所用的是字节流. 优化: A:这次,为了高效,对这个字节流通过转换流来进行包装,包装成高效字符流. B:这次,传输的数据是通过键盘录入的数据. 服务器 ...

  5. java 26 - 6 网络编程之 TCP协议 传输思路 以及 代码

    TCP传输 Socket和ServerSocket 建立客户端和服务器 建立连接后,通过Socket中的IO流进行数据的传输 关闭socket 同样,客户端与服务器是两个独立的应用程序 TCP协议发送 ...

  6. python六十九课——网络编程之TCP协议

    1.1 概述: TCP协议通过三次握手协议将客户端与服务器端连接,两端使用各自的Socket对象.Socket对象中包含了IO流,供数据传输. 即:TCP协议在客户端与服务器端通过Socket组成了I ...

  7. java 26 - 9 网络编程之 TCP协议多用户上传文件

    TCP实现多用户上传文件: 需要同时给多用户上传文件,这样就得用多线程来实现. 实际上,这样的话,上传的先后顺序和速度就跟客户端的带宽有关:带宽够,就容易抢占到线程的执行权: 首先,创建个线程类:(这 ...

  8. java 26 - 8 网络编程之 TCP协议的练习

    TCP练习: 1.客户端键盘录入,服务器输出文本文件 客户端代码: public class ClientDemo { public static void main(String[] args) t ...

  9. java 26 - 8 网络编程之 TCP协议上传图片

    上次的是上传TXT文件,这次上传的是图片.同样,上传成功需要反馈给客户端. 区别: TXT文件用记事本打开,我们可以看得懂,所以用了缓冲字符流,对通道内的字节流进行包装了. 而图片用记事本打开,我们看 ...

随机推荐

  1. 不获取元素,直接使用id操作dom元素

    今天无意中发现个让我很吃惊的问题. 不使用getElementById方法,也可以用id直接操作有id的元素. 继续搜索后,发现name也可以直接操作... 这让我大感意外,了解以后,忍不住写点东西记 ...

  2. 第一次作业--Numpy练习

    1.创建一个边界值为1而内部都是0的数组,图例如下:[提示:]解此题可以先把所有值都设置为1,这是大正方形:其次,把边界除外小正方形全部设置为0.本题用到numpy的切片原理.多维数组同样遵循x[st ...

  3. Codeforces Round #596 (Div. 2, based on Technocup 2020 Elimination Round 2) B. TV Subscriptions 尺取法

    B2. TV Subscriptions (Hard Version) The only difference between easy and hard versions is constraint ...

  4. 9.27 csp-s模拟测试53 u+v+w

    T1 u 拿到题感觉他很水,但到死没想到正解,只会骗部分分(我太弱了) 考虑用两个差分数组维护,不同的是最后更新答案是$a[i][j]+=a[i-1][j-1]$,首先考虑在斜着加的起点,就是竖着的直 ...

  5. Unity开发实战探讨-资源的加载释放最佳策略简要心得

    Unity开发实战探讨-资源的加载释放最佳策略简要心得 看过我另外一篇关于Unity资源释放随笔<Unity开发实战探讨-资源的加载释放最佳策略>如果觉得略微复杂,那么下面是一些比较简要的 ...

  6. 记一次Lua语言中死循环查错

    前言 如果在Lua语言中某一处死循环了!你特么的怎么去查出这特么的该死的循环到底在特么的哪里!!! 重现步骤 一打开技能界面,整个游戏就卡死不动了 开始排查 查看一下cpu占用率,unity占用60% ...

  7. Python连载32-多线程其他属性以及继承Thread类

    一.线程常用属性 1.threading.currentThread:返回当前线程变量 2.threading.enumerate:返回一个包含正在运行的线程的list,正在运行的线程指的是线程启动后 ...

  8. JeeSite | 数据权限应用

    中午吃饭时看了一下陆毅版的<三国>,刚好看的是蜀军缺粮,诸葛亮让王平去劫司马懿的粮.司马懿看蜀军用木牛流马运量很方便,就抢了蜀军的木牛流马仿制了一批,结果司马懿用它运粮时,被王平冒充司马懿 ...

  9. 博云 x 某农商行 | 银行信息化运维系统升级的最佳实践

    随着银行新一代信息化运维系统建设的推进,应用系统更新换代速度明显提升.数字化转型的发展对银行业务需求的敏捷性提出了越来越高的要求,促进敏捷开发和资源敏捷部署成为大势所趋. 背景 江苏某农村商业银行成立 ...

  10. tensorflow之tf.stop_gradient

    停止梯度计算. 当在一个图中执行时, 这个op按原样输出它的输入张量. 当构建ops来计算梯度时,该op会阻止将其输入贡献考虑在内. 参数: Input: 一个张量. name: 操作的名称(可选) ...