参考链接 :
http chunked : http://blog.csdn.net/yaneng/article/details/4378984
zlib压缩和解压gzip : http://www.oschina.net/code/snippet_65636_22542
请多多支持以上作者,谢谢...
前几天写了这个主题,但是那个时候思路不清晰,写的乱七八糟.写博客为的就是理清思路.并且程序的bug也有很多,实在误人误己,所以删掉之前的那篇,从新写一篇.
遗憾上传的附件没有办法删除.
linux下并没有什么比较牛的翻译软件,之前youdao倒是做了一个,但是有个细节处理的不是很好,正好挡住vim的状态栏最右边的一部分,导致组合命令用起来很不方便,就放弃了.但是最近总是看英文文档,对翻译软件的需求还是有的.所以就萌生了一个自己做一个的想法.用了一个星期,总算初见成效.
这一步比较简单,调用几个API就可以了.
代码片段 :
// 域名对应IP struct hostent *ent; ent = gethostbyname(host); if (NULL == ent || NULL == *ent->h_addr_list) { perror("\n"); return -1; }
// 非阻塞连接服务器 int ret = 0; int flag = 0; int sockfd = 0; struct sockaddr_in addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) { perror("\n"); return -1; } addr.sin_family = AF_INET; addr.sin_port = htons(80); addr.sin_addr.s_addr = naddr; flag = fcntl(sockfd, F_GETFL, 0); ret = fcntl(sockfd, F_SETFL, flag | O_NONBLOCK); if (ret < 0) { close(sockfd); return -1; } ret = connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); if (ret < 0) { if (errno != EINPROGRESS) { close(sockfd); perror("\n"); return -1; } } else if (ret == 0) { goto DONE; } fd_set wset; fd_set tmpset; struct timeval tv; FD_ZERO(&wset); FD_SET(sockfd, &wset); tv.tv_sec = 10; tv.tv_usec = 0; while (1) { tmpset = wset; ret = select(sockfd + 1, NULL, &tmpset, NULL, &tv); if (ret < 0) { if (EINTR == errno) { continue; } else { close(sockfd); perror("\n"); return -1; } } else if (0 == ret) { close(sockfd); return -1; } break; } DONE: return sockfd;
我并不知道该给有道发什么消息,所以就在网页上面翻译了一个单词,通过wireshark查看,去掉cookie等信息如下:
POST /translate?smartresult=dict&smartresult=rule&smartresult=ugc&sessionFrom=null HTTP/1.1\r\n\ Host: fanyi.youdao.com\r\n\ Connection: keep-alive\r\n\ Content-Length: %d\r\n\ Accept: application/json, text/javascript, */*; q=0.01\r\n\ Origin: http://fanyi.youdao.com\r\n\ X-Requested-With: XMLHttpRequest\r\n\ User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\r\n\ Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n\ Referer: http://fanyi.youdao.com/\r\n\ Accept-Encoding: gzip, deflate\r\n\ Accept-Language: en-US,en;q=0.8\r\n\ \r\n\ type=AUTO&i=%s&doctype=json&xmlVersion=1.8&keyfrom=fanyi.web&ue=UTF-8&action=FY_BY_CLICKBUTTON&typoResult=true";
分析一下这个请求可以发现
a. 服务器给我们返回的是json的格式.我们得支持gzip压缩.
b. i=%s : 这里就是我们要存放翻译原文的地方.如果是英文,直接原文即可,但是对于汉字,这里的处理有点特别.一个中文的utf-8是三个字节,但是有道的请求里面,一个中文占六个字节,比如快乐 :正常的utf-8编码应该是 %E5%BF%AB%E4%B9%90 , 但是有道里面是这个样子的%C3%A5%C2%BF%C2%AB%C3%A4%C2%B9%C2%90 .网上搜了很久都没有找到问题的原因,后来FQ用谷歌找到了问题的答案,原来是因为js的一个函数EncodeURI,这个函数会把汉字给转成6个字节.对应表在代码的encoding.h里面,
c. 其实这里我被浏览器坑了,Accept-Encoding这个,如果不加,有道会返回一个非gzip压缩的明文格式,结果我刚开始不知道,以为是必须的,然后就有了各种纠结解压缩.不过好处当然也显而易见,对zlib又有了新的理解.d. 另外就是有道也在这里坑了一把,明明我有keep-alive,结果它并没有,每次请求完,它立刻关闭连接.导致我下一次请求直接RST了.这个异常的处理方式是,如果write失败,就退出.
参考unp,写一个关闭的套结字会收到RST,如果不管这个RST继续写入,程序就会崩溃,因为收到了SIGPIPE,如果不想被异常终止,需要捕获SIGPIPE,或者忽略它.
这里很明显我的设计存在一个bug的,我只固定读取了给定大小.因为我的场景不会有大篇幅的翻译.
如果说需要大篇幅,那么正确的做法应该是申请一个大buffer,接收数据,根据chunked判断有没有到达消息尾部,如果没有到达而buffer满了,先追加到某个文件里面,继续读...
关于chunked,文首的连接想必说的很清楚了,明确chunked格式,还是不难解的,比较坑爹的地方是,每一段chunked长度是用ascii码保存的.我开始理解错了,以为是直接转成数字就可用,结果走了一段弯路.参考代码片段 :
int i = 0; int len = 0; int power = 0; int dec = 0; int seed = 0; len = strlen(hex); for (i = len - 1; i >= 0; i--) { power = len - i - 1; switch (hex[i]) { case '0': { seed = 0; } break; case '1': { seed = 1; } break; case '2': { seed = 2; } break; case '3': { seed = 3; } break; case '4': { seed = 4; } break; case '5': { seed = 5; } break; case '6': { seed = 6; } break; case '7': { seed = 7; } break; case '8': { seed = 8; } break; case '9': { seed = 9; } break; case 'a': { seed = 10; } break; case 'b': { seed = 11; } break; case 'c': { seed = 12; } break; case 'd': { seed = 13; } break; case 'e': { seed = 14; } break; case 'f': { seed = 15; } break; default: break; } dec += seed * pow(16, power); } return dec;
关于解压缩,我就不过多描述了,参考我的另一片文章,借鉴了很多网友的成果,还是有一定参考性的.
http://blog.csdn.net/cp3alai/article/details/51282338
解析工具选择了cJSON, 源码路径 : https://github.com/DaveGamble/cJSON.git .这里面的例子 test.c 很清晰易懂.
我本来想再封装一层,无奈又是object又是数组,需要调用的方法又不一样,失败.只能傻瓜一样,按顺序找到我需要的解释.
1. 接收数据的缓冲区写死了,如果需要翻译的数据足够大,可能就完蛋了.
2. html是chunked的格式,但是我只解了第一个chunk,如果需要翻译的数据足够大,还是会有问题的.
使用如图 :
更新了以后,稳定多了,原来上传的那个没有办法删除,只能从新再传一个了...
http://download.csdn.net/detail/cp3alai/9509158