一。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. Django cache (缓存)

    五种配置 1.开发调试 # 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backend ...

  2. ubuntu16.04下安装nvidia驱动心得

    首先机器重启后莫名出现循环登录错误,然后按照网上的方法卸载掉nvidia驱动后,可以正常登录. 但还是要再装nvidia驱动.网上说的各种方法都试过了,geforce.cn官网上推荐的各种版本的run ...

  3. FineUIPro v6.0.1 小版本更新!

    这次修正了 v6.0.0版本的几个问题,建议所有用户升级到此版本: +修正调用F.addMainTab时可能出现JS错误的问题(34484135,1450561644).    -仅在未调用F.ini ...

  4. resharper 8.2

    用户名:ronle注册码:ZoJzmeVBoAv9Sskw76emgksMMFiLn4NM

  5. Flink之state processor api实践

    前不久,Flink社区发布了FLink 1.9版本,在其中包含了一个很重要的新特性,即state processor api,这个框架支持对checkpoint和savepoint进行操作,包括读取. ...

  6. ELK 日志平台 For Windows

    一.Logstash 安装 1. 下载最新版本的logstash:  https://www.elastic.co/fr/downloads/logstash 下载zip格式的压缩包. 然后解压缩放到 ...

  7. Could not find resource——mybatis 找不到映射器xml文件

    今天用IDEA写Mybatis的时候,测试报了如图所示的错,恶心死我了,后来解决了,总结一下,防止下回跳坑,当然,也是做一个分享,如果有朋友遇到这个错,希望有所帮助 Error parsing SQL ...

  8. IDEA的常用配置(Maven)一键导入及优化内存

    IDEA的常用配置一键导入 一.在https://www.cnblogs.com/zyx110/p/10799387.html中下载如图的压缩包 下载完成后解压缩,点击settings_bak,你会看 ...

  9. Zabbix-proxy和Zabbix-agent源码安装

    一 .Zabbix Proxy 概述 Zabbix proxy 是一个可以从一个或多个受监控设备采集监控数据并将信息发送到 Zabbix server 的进程,主要是代表 Zabbix server ...

  10. RFC函数的初步使用-同步

    1.由于没有外围系统,采用不同SAP不同client之间进行测试. 首先在A-client搭建需要被调用的RFC函数.在A-client里运行SE37创建函数 在属性页签选择“远程启用的模块” 设定i ...