一。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. IOI2015 boxes纪念品盒

    BZOJ 4368: [IOI2015]boxes纪念品盒 BZOJ传送门 Description IOI2015开幕式正在进行最后一个环节.按计划在开幕式期间,每个代表队都将收到由主办方发放的一个装 ...

  2. 逐行剖析Vue源码(一)——写在最前面

    1. 前言 博主作为一名前端开发,日常开发的技术栈是Vue,并且用Vue开发也有一年多了,对其用法也较为熟练了,但是对各种用法和各种api使用都是只知其然而不知其所以然,因此,有时候在排查bug的时候 ...

  3. js json字符串与json对象互相转换(最全)

      1.json字符串转json对象 使用场景:通常在取json字符串里具体的值时,会用到. var jsonString = '{"name":"Marydon&quo ...

  4. 数据仓库006 - MySQL 5.6.x - Linux最佳生产环境离线部署

    一.离线安装包 文件准备 这里以mysql-5.6.23-linux-glibc2.5-x86_64.tar.gz为例,记一次MySQL 5.6.x 的生产环境离线部署过程.使用SecureCRT连接 ...

  5. Anaconda入门教程【快速掌握】

    Anaconda 使用指南 概述 很多学习python的初学者甚至学了有一段时间的人接触到anaconda或者其他虚拟环境工具时觉得无从下手, 其主要原因就是不明白这些工具究竟有什么用, 是用来做什么 ...

  6. Windows7运行python3,提示缺少api-ms-win-crt-runtime-l1-1.0.dll

    一.实验环境 1.Windows7x64_SP1 二.操作步骤 2.1 python官网下载python3.6后,安装.运行,提示如下错误: 2.2 解决方式 去微软官网下载安装:KB2999226补 ...

  7. 一.OS运行机制

    运行机制: 1.中断(外部) =====一种通知行为(例如插入键盘) 2.系统调用(主动反应) ===一种请求行为 3.异常(内部) =====一种错误处理行为 系统调用和程序接口的关系,接口把系统调 ...

  8. WPF 中如何变相让 ListBox 宽度(Width) 100%,高度(Height) 100%,从而达到 Filled 的效果

    直接贴代码了: XAML: <Window x:Class="HelloWorld.MainWindow" xmlns="http://schemas.micros ...

  9. jieba分词原理-DAG(NO HMM)

    最近公司在做一个推荐系统,让我给论坛上的帖子找关键字,当时给我说让我用jieba分词,我周末回去看了看,感觉不错,还学习了一下具体的原理 首先,通过正则表达式,将文章内容切分,形成一个句子数组,这个比 ...

  10. deepin把vscode设为默认文本应用

    一开始我想自己写一个desktop文件放在/usr/share/applications下面,结果在右键菜单里面找不到vscode. [Desktop Entry] Categories=Develo ...