收录日期:2019/01/21 20:17:44 时间:2016/07/26 20:16:53 标签:应用程序开发区
在AIX机器上接收客户端的报文,将其中的图像处理之后返回给客户端。数据量特大。

怎么保证顺利接收和发送。
如果采用非阻塞方式,则返回给客户端的数据量太太,远超过发送buffer大小(设太太大肯定不行),分开发送,接收端接收到的数据不对,如果采用阻塞方式的话,如果客户端告知的数据长度比实际发送来的长度大,则导致一直等待客户端发送数据。


有什么好的解决办法吗?

谢谢。




现在用的是非阻塞方式。
int value = 1;
ioctl(sockfd, FIONBIO, (char *)&value, sizeof(value));
然后又设置了发送buffer和接收buffer的大小。

可是我现在需要的是发送大块的图像数据,可能达10M。
发送的时候,如果分多次发送,则接收端会多收到一些数据,而且是在两次发送中间。
发送代码如下:
long len = image_len;
long rest = len;
while(rest > 0)
{
  count = send(sockfd, ptr, SEND_BUFFER_SIZE < rest ? SEND_BUFFER_SIZE : rest, 0);
  ptr += count;
  rst -= count;
}

发送完之后,在服务端没发现异常,在客户端(windows上)接收到的数据,则比发送的要多,而且图像跟服务端发送的图像不一像了,多了一些黑块。而且每次接收到的大小会变化。


该怎么办呢?

之所以采用非阻塞方式,因为如果采用阻塞方式的话,接收会有问题:
接收格式为:长度,数据。
如果客户端发送的报文中长度比实际发送的数据要大的话,导致阻塞。
long len = image_len;
long rest = len;
while(rest > 0)
{
  count = read();
  rest -= count;
}
如果len = 10000, 实际发过来的数据长度为8000,则会阻塞在count = read()语句上。


请大虾赐教。如果采用阻塞方式,则需解决接收的问题,如果采用非阻塞方式,则需解决发送的问题。

另外,不可能采用分包方式。不是技术原因。
如果设置成阻塞模式,设置接收的超时选项,或者用alarm设置定时器,如何?
用alarm怎样单纯地从recv()函数中跳出?
struct timeval timeout;
/* 30 Secs Timeout */
timeout.tv_sec = 30;
timeout.tv_usec = 0;

setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,(struct timeval *)&timeout, sizeof(struct timeval));
设置SO_RCVTIMEO,
如果有数据read/recv会返回已经读到的数据的字节数,
如果没有数据会返回-1并设置errno为EAGAIN或者EWOULDBLOCK
引用楼主 liaozhicai 的回复:
如果客户端发送的报文中长度比实际发送的数据要大的话,导致阻塞。

客户端发送的长度??不是服务器发送报文吗?
引用 4 楼 justkk 的回复:
引用楼主 liaozhicai 的回复:如果客户端发送的报文中长度比实际发送的数据要大的话,导致阻塞。
客户端发送的长度??不是服务器发送报文吗?


客户端发送报文给服务器,格式是[二进制数据长度][二进制数据]
服务器将二进制数据处理后按约定的格式返回给客户端。

如果客户端发送的报文有误,如[10][111100001],
报文中长度字段解析出来为10,但其后的数据实际只有9,使用
while(rest>0)
{
  count = read();
  rest -= count;
}
接收数据。count =9, rest = 1;之后进入while(rest>0)循环,阻塞在count = read()语句上。
(实际中可能数据长度可能是几百万之多,故不可能一次性接收,只好通过while循环)。



目前我是用alarm解决了问题,通过设置SO_RCVTIMEO参数不管用,因为实际系统为AIX5.3,一查man setsockopt,结果告知:SO_RCVTIMEO is setable, but unuseable.


通过alarm直接退出进程。但希望能通过alarm直接跳出read()语句。
你这根本原因还是客户端发送的长度有误啊,明明只有8个字节,非说发送了10个字节..

抛开客户端不管,服务端想退出read()也是可以的..
比较适合的方法是使用select()

使用alarm()也可以,调用简单一些,你需要设置信号处理动作
在你的程序中添加信号处理函数的定义,空的函数体就行 void foo(int sig) {}
然后signal(SIGALRM, foo);
引用 6 楼 justkk 的回复:
你这根本原因还是客户端发送的长度有误啊,明明只有8个字节,非说发送了10个字节..

抛开客户端不管,服务端想退出read()也是可以的..
比较适合的方法是使用select()

使用alarm()也可以,调用简单一些,你需要设置信号处理动作
在你的程序中添加信号处理函数的定义,空的函数体就行 void foo(int sig) {}
然后signal(SIGALRM, foo);……



我现在用的就是select,用的是阻塞式的select,如果用非阻塞式的select,是没问题的,但用非阻塞式的select的话,用while(rest > 0)循环发送大容量数据,结果在客户端接收到的数据比发送的还多,而且每次收到的数量还不一定相同,所以才用阻塞式的select,而且用非阻塞式的select,由于EWOULDBLOCK次数太多,结果比用阻塞式的select要慢不少。
引用 6 楼 justkk 的回复:
你这根本原因还是客户端发送的长度有误啊,明明只有8个字节,非说发送了10个字节..

抛开客户端不管,服务端想退出read()也是可以的..
比较适合的方法是使用select()

使用alarm()也可以,调用简单一些,你需要设置信号处理动作
在你的程序中添加信号处理函数的定义,空的函数体就行 void foo(int sig) {}
然后signal(SIGALRM, foo);……



空的alarm响应函数不行的吧,我试了一下,不退出进程,打印后直接return,结果进程还是阻塞的
引用 7 楼 liaozhicai 的回复:
结果在客户端接收到的数据比发送的还多,而且每次收到的数量还不一定相同

这是你程序逻辑问题了,这怎么可能??
引用 7 楼 liaozhicai 的回复:
所以才用阻塞式的select,而且用非阻塞式的select,由于EWOULDBLOCK次数太多,结果比用阻塞式的select要慢不少

阻塞式的select?? 非阻塞式的select??
只听说过socket本身的阻塞与否
使用select的目的就是为了避免socket操作的阻塞,select用来判断socket描述符是否可读/可写,select返回之后,程序可以操作描述符,这时候怎么还会EWOULDBLOCK呢??
引用 8 楼 liaozhicai 的回复:
空的alarm响应函数不行的吧,我试了一下,不退出进程,打印后直接return,结果进程还是阻塞的

信号的目的是中断read操作,此时read会返回一个错误,怎么处理这个错误取决于你的程序自己了。。
引用 10 楼 justkk 的回复:
引用 8 楼 liaozhicai 的回复:空的alarm响应函数不行的吧,我试了一下,不退出进程,打印后直接return,结果进程还是阻塞的
信号的目的是中断read操作,此时read会返回一个错误,怎么处理这个错误取决于你的程序自己了。。



非常谢谢。

我再仔细设计一下程序结构。



如何操作二进制字段???? shellexcute? 哪里有好的版本控制软件???? 星心,对不起!真是报歉!!!!!!八哥,你见了也给我说说好话吧,向他!!!! 大侠们,如何取得所在局域网的所有电脑的名字? 如何实现在256色下抖动显示24bit图像,不用drawdibdraw,有算法最好,先谢了 随机函数怎么写??????? 喜啊~喜!跟大学最好的弟兄恢复了联系,特送分庆祝。 各地女孩是怎么骂男孩的?(COPY/PASTE) 如何实现在256色下抖动显示24bit图像,不用drawdibdraw,希望告知详细算法,先谢了 请教各位:我用ODBC的CRecordSet类,如何获得表中某列数据的总和? 各位为什么我引用JAVABEAN出错,麻烦各位看看。 菜鸟提问(一):偶刚接触bcb,请教一些问题 哪位知道YGL表示甚麼意思??? 小弟最近有一个问题,百思不得其解 谁知道如何取得局域网中所有电脑的名字? Message Maps 中为什么找不到 OnWindowPosChanging ? 请问用PB6.5的有多少人?7.0的呢?我该不该升级呢? 超强送分!!!!是微软呢还是delphi的bug??????????????????????????????????? 各位打星际的兄弟看过来! RED HAT中KDE和GNOME下的应用程序的源程序该如何找到? 各路高手看过来。请谈一下视频广播的实现方法好吗?谢谢。。 建议开设 ASP.NET ,回复有分(只有105分) 打倒火的天空,竟然给可我一个可以死机的网页 to tml 列举进程的问题 关于MDI Child的DLL调用 上网密码怎么保存不了? 高手请进,非高手莫入,以免浪费时间! guo(正在学Oracle和Solaris) 你好