一个简单的文件传输验证码识别c/s实现

大体思路

client通过tcp向server传输一个待识别的验证图片,server端识别后通过tcp回传给client。

server端识别验证码采用pytesseract。

环境:server/python3.5/ubuntu1604,client/Qt5.3/win7。

1、文件传输

tcp很多人都很熟悉,经常用来传递数据。但如何用于传递文件呢。尝试过的同学可能知道常见的有这么几个问题:

1)文件较大时tcp会分包。

一张图片可能2M甚至更大,当然普通的验证码截图很小400k左右。但考虑到健壮性、通用性,还是应当通过设计来解决这个问题。

2)文件长度不对可能导致损坏

一张图片如果头部少了或者多了一些信息是很有可能导致图片无法打开,更无从说识别。而尾部多了少了相对不那么敏感,但最好不要弄错。试想,如果客户的照片效果很好,由于你在传递过程中多接收或者少接收数据导致打了折扣,加班说不定又要开始了。所以,还是精确为好。

而且如果一个文件是1800字节,一包tcp一般1500字节以内,你rcv一次直接存文件,文件够呛能打开。而如果是300字节的文件,你直接写进去1400字节,肯定有问题。

所以,文件长度是文件传输的一个必须前知的参数。

针对这两个问题,用协议来解决。此处自定义了一个很简单的协议如下:

filename seperator filelen seperator filecontent

也就是将传输的内容分成3段,第一段是文件名,第二段是文件长度,第三段是文件内容。都是以字节流的形式封装传递。

各段用分隔符加以分隔,此处为一个tab。

2、server端代码

刚过完年,笔记本没装pycharm,代码纯用vim写的,所以注释空格很多未符合pep8规则的地方,大家自行忽略。。。vim的插件配置还在摸索。。。

#encoding:utf-8

#!/usr/bin/env python

#导入socket模块

import socket

import sys

import pytesseract

from PIL import Image

#添加异常处理,添加tcp断链处理与保护判断

def getCode(path):

""" get identify code with pytesseract """

img=Image.open(path)

vcode=pytesseract.image_to_string(img)

print(vcode)

return vcode

class Proto:

def init(self):

print("hehe")

def decode(self,cont):

print("decode in parent")

#从第一包数据解析出文件名、长度、文件内容起始处

class HeadAnalyzor(Proto):

def init(self):

contPos=0

contLen=0

name=""

def decode(self,cont):

print(cont)

nameEnd=cont.find(bytes(' ','utf8'))

self.name=cont[0:nameEnd]

self.contPos=cont.find(bytes(' ','utf8'),nameEnd+1)

print("na %d co %d" %(nameEnd,self.contPos))

self.contLen=(int)(cont[nameEnd:self.contPos])

print("nameEnd %d contPos %d contLen %d" %(nameEnd,self.contLen,self.contLen))

#将文件内容存入文件中

class FileTransferProto(Proto):

def decode(self,cont):

print(type(cont))

pos=cont.find(bytes(' ','utf8'))

name=cont[0:pos]

pos=cont.find(bytes(' ','utf8'),pos+1)

print(" pos is %d" %(pos))

if pos>0:

f=open(name,'wb')

print("fName is %s len is %d" %(name,len(cont)-pos))

f.write(cont[pos+1:-1])

f.close()

else:

print("not found space ")

#开启ip和端口

ip_port = ('192.168.65.49',(int)(sys.argv[1]))

#生成句柄

web = socket.socket()

#绑定端口

web.bind(ip_port)

#最多连接数

web.listen(5)

#等待信息

print ('nginx waiting...')

#阻塞

conn,addr = web.accept()

print("accepted")

#开启死循环

while True:

print("again")

#获取客户端请求数据

data = conn.recv(1024000)

buf = data

#客户端断开链接

if len(data)==0:

print("disconnect by peer! rcvLen:%d \n waiting..." %len(data))

#重连

conn,addr=web.accept()

continue

headAna = HeadAnalyzor()

headAna.decode(data)

remainLen=headAna.contLen -len(data) + headAna.contPos + 1

rcvLen=len(data)

i=0

#直到指定长度的文件内容均接收到后进行存文件操作

while remainLen>0:

data=conn.recv(1024)

rcvLen+=len(data)

remainLen-=len(data)

print("times %d remain %d conLen %d rcvLen %d" %(i,remainLen,headAna.contLen,rcvLen))

i+=1

buf+=data

proto=FileTransferProto()

proto.decode(buf)

#向对方发送数据,也即识别出来的验证码

idcode=getCode(headAna.name)

print(conn.send(bytes(idcode,'utf8')))

#关闭链接

# conn.close()

运行脚本:

python3 tcpserver.py 8889

3、client端代码

基于Qt写的,此处摘写代码片段。

1)、读文件封装数据

int QEasyWrapper::wrap(char *buf, std::size_t maxlen)

{

memset(buf,0,maxlen);

QFile f(fPath_.c_str());

f.open(QIODevice::ReadOnly);

QByteArray ba=f.readAll();

std::cout<

f.close();

if(0>= ba.size()){

std::cout<

return -1;

}

sprintf(buf,"%s%c%d%c",fName_.c_str(),sep_,ba.size(),sep_);

int headLen=strlen(buf);

int cpyLen=std::min(maxlen,ba.length()+headLen);

memcpy(&buf[headLen],ba.data(),cpyLen);

std::cout<

return cpyLen;

}

2)、利用QTcpSocket进行数据的传输与接收

发送:

int TcpFileSender::write(char *buf, std::size_t len)

{

int sndLen=sock->write(buf,len);

std::cout<waitForBytesWritten(5000)<<:endl>

std::cout<

return sndLen;

}

接收:

int TcpFileSender::read(char *buf, std::size_t buflen)

{

std::cout<waitForReadyRead(5000)<<:endl>

if(rcvBuf_.size()<1 || buflen<1)

return 0;

QByteArray ba=sock->readAll();

std::cout<

int cpyLen=std::min<:size_t>(buflen,ba.size());

memcpy(buf,ba.data(),cpyLen);

return cpyLen;

}

tips

QtcpSocket在使用过程中发送后与接收前一定要成套调用waitfor相关的函数。

如果不调用,发送之后立即调用接收的读相关函数,接收到的将是空,因为write和read 这2个接口是非阻塞的,直接执行,发还没发完呢就开始读了,当然收不到数据。当然,用connect将ReadyRead信号绑定到一个函数进行接收是可以收到数的。

还有一种情况,write之后未加waitfor,read之前加了waitfor,会报超时。因为你发的时候直接就返回,未等待其执行完毕,然后调用waitForReadyRead,直接将socket阻塞掉了,相当于发了一半被waitForReadyRead阻塞了,服务端未收齐数据自然不会回复。这样最终就超时了。

针对这个细节,个人理解,QTcpSocket底层做了一个“线程”封装进行数据的收发,waitFor相关函数会阻塞此线程。而且直接在主线程中调用Sleep也是不行的。此“线程”还非彼线程,会随主线程一起被阻塞。待有机会再研究下。欢迎熟悉底层或者源码的同学拍砖指正。 :)

c 语言 验证码识别算法,一个简单的文件传输验证码识别c/s实现相关推荐

  1. 用C语言或C++编写一个简单的银行家算法模拟程序

    1.问题描述 银行家算法是操作系统中避免死锁的典型算法.用C语言或C++编写一个简单的银行家算法模拟程序,实现多个进程争用系统临界资源时的分配过程.要求程序实现: 1.当一进程请求一组资源时,先确定是 ...

  2. c语言编程坦克图案,用C语言的图像函数画一个简单的坦克图样

    用C语言的图像函数画一个简单的坦克图样 #include "graphics.h" #include "conio.h" void Tanke(int x,in ...

  3. java 语言 写字板_一个简单的java语言写字板.docx

    一个简单的java语言写字板.docx 一个简单的JAVA语言写字板一.需求分析1.需求分析:现在网络上各种文档编辑器数不胜数.功能也是应有尽有,有能改变字体的,有可以改变字体颜色的,但是,这些软件有 ...

  4. 函数式编程中的战斗机(二) --运用elm语言MUV设计模式做一个简单的应用实例

    @函数式编程中的战斗机(二) -运用elm语言MUV设计模式做一个简单的应用实例 1 elm语言设计模式的特点 1.1 面向对象设计模式的特点 每种编程语言都有其独特的语法和优缺点,从而导致与众不同的 ...

  5. C语言学习笔记---一个简单的文件压缩示例

      通过一个简单的文件文件压缩例子,来学习文件操作的相关函数.该程序以只读的方式 "r" 打开第一个文件,以只写的方式"w"打开第二个文件.将第一个文件中每隔3 ...

  6. 怎样用java写一个简单的文件复制程序

    怎样用java写一个简单的文件复制程序 代码来源:https://jingyan.baidu.com/article/c35dbcb0d6f1398916fcbc07.html package Num ...

  7. 20. [Python GUI] PyQt5中的模型与视图框架-实现一个简单的文件浏览器的例子

    PyQt5中的模型与视图框架-实现一个简单的文件浏览器的例子 一.使用模型/视图实现一个简单的文件浏览器 二.小手一抖,点个赞再走哦~ 一.使用模型/视图实现一个简单的文件浏览器 这个例子里不涉及数据 ...

  8. 一个简单的P2P传输程序

    写了一个简单的P2P传输程序,在P2P的圈子中传输文件,不过为了简便,这个程序没有真正的传输文件,只是简单的判断一下文件的位置在哪里.这个程序可以处理当有一个peer闪退的情况,在这种情况下,剩下的p ...

  9. 使用kNN算法实现简单的手写文字识别

    0. 介绍 kNN,即k-Nearest Neighbor(k近邻算法), 简介可参考KNN的一些总结. 本文是<机器学习实战>一书第二章的例子, 主要利用kNN实现简单的手写文字识别. ...

最新文章

  1. MD5加密字符串并转化为base64(C#和PHP代码相同实现)
  2. 2012-02-14 貌似情人节
  3. Vue2.0中子组件向父组件传递数据的方法,以完整demo演示
  4. Linux 给用户添加sudo权限
  5. Linux学习之服务器搭建——DHCP服务器
  6. python随机产生100个整数二进制_PYTHON练习题 二. 使用random中的randint函数随机生成一个1~100之间的预设整数让用户键盘输入所猜的数。...
  7. 数据揭秘中国女性的薪水普遍比男性低,说好的“同工同酬”呢?
  8. 笔记 英语二 考研先导课 0126
  9. linux redis客户端_10个 Linux 顶级开源缓存工具
  10. JavaScript 将死?
  11. oracle ash dump 导出,Oracle 导出 ASH的dump信息
  12. 蓝桥杯单片机历年真题答案
  13. forEach 终止循环
  14. 20220721挨揍内容
  15. php+aira2+ffmpeg下载m3u8文件并保存成mp4
  16. 网站被攻击如何修复网站漏洞
  17. objdump指令 elf文件转成lst文件
  18. 从0开始安装k8s1.25【最新k8s版本——20220904】
  19. Lazada卖家上传产品实操,采采Lazada上货铺货助手,一键采集刊登上架,批量上品删除宝贝上下架更改产品属性,自动翻译,图片文字翻译
  20. cocos2d-x 中文输出 GB2312与UFT8互转

热门文章

  1. 美团前端二面必会面试题(附答案)
  2. 香港高防服务器的常规防御方式
  3. 小白重装系统小白必看——镜像文件选择
  4. 缠论指标【缠论指标】
  5. 【Netty系列_3】Netty源码分析之服务端channel
  6. c语言3d打印机控制程序下载,MatterControl(3D打印机控制软件)
  7. 机器人 郑佳佳_浙大硕士暗恋一女子 竟和同名机器人举行婚礼
  8. 机器人 郑佳佳_浙大硕士被暗恋对象拒绝后 和自己造的“女友”结婚了
  9. IGARSS 会议介绍
  10. GNSS及其定位原理,差分GNSS技术分析