免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 5503 | 回复: 10
打印 上一主题 下一主题

LINUX下TCP程序问题,为何服务端会延迟一会儿收到客户端的数据 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-02-07 09:47 |只看该作者 |倒序浏览
简单点说,就是客户端发送a,服务端没收到任何东西,客户端发送b,客户端收到a,客户端发送c,服务端收到b
下面是详细的情况

linux下的c程序作为服务端,客户端是java程序。
服务端使用非阻塞socket,每隔一秒定时接收一次,客户端每隔5秒发送心跳信息,此外客户端不定时发送指令数据,要求服务端回应此指令。

现在的问题是服务端经常会延迟(远大于1秒)收到客户端的指令,若客户端关闭发送心跳数据,则服务端经常会在客户端第二次发送指令的时候才收到第一次的数据。似乎是后一次发送的数据将前一次的数据由协议层‘挤’到socket层的。

后来我又把socket改为阻塞的,采用线程方式,效果类似,不知道原因在哪里?

代码见下面

服务端socket初始化部分
        if (srvsock > 0)
        {
                shutdown(srvsock, SHUT_RDWR);
                close(srvsock);
        }

        srvsock = socket(AF_INET, SOCK_STREAM, 0);
        if (srvsock < 0)
                return (BOOLEAN)1;              /*log it */
        i = fcntl(srvsock, F_SETFL, O_NONBLOCK);
        if (i < 0)
        {
                shutdown(srvsock, SHUT_RDWR);
                close(srvsock);
                return (BOOLEAN)1;      /*log it */
        }
        k = 1;
        v = 1;

        i = setsockopt(srvsock, SOL_SOCKET, SO_REUSEADDR, &k, sizeof(int));
        if (i < 0)
        {
                shutdown(srvsock, SHUT_RDWR);
                close(srvsock);
                return (BOOLEAN)1;      /*log it */
        }

        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(MTRACE_TCP_PORT);
        i = bind(srvsock, (struct sockaddr *)&local, sizeof(local));
        if (i < 0)
        {
                shutdown(srvsock, SHUT_RDWR);
                close(srvsock);
                return (BOOLEAN)1;      /*log it */
        }
        i = listen(srvsock, MAX_CLIENT_NUM);
        if (i < 0)
        {
                shutdown(srvsock, SHUT_RDWR);
                close(srvsock);
                return (BOOLEAN)1;      /*log it */
}

接收部分
                        while ((j = recv(sockid, msgbuf, sizeof(msgbuf), 0/*MSG_NOSIGNAL*/)) > 0)
                        {
/ *process... */
                        }
                        if (j == 0)             /*peer closed */
                        {
/* close */
                        }
                        else if (j < 0)
                        {
                                if (errno != EAGAIN)
                                {
/* close */
                                }
                        }



了socket section 7,上面说
SO_RCVLOWAT and SO_SNDLOWAT
     Specify  the minimum number of bytes in the buffer until the socket layer will pass the data to the protocol (SO_SNDLOWAT) or the user on receiving (SO_RCVLOWAT).  These two values are  not  changeable  in  Linux  and  their  argument  size  is  always  fixed to 1 byte.  getsockopt is able to read them; setsockopt will always  return ENOPROTOOPT.

抓包器抓包的结果可以确定客户端发的数据已经发送到了服务端的机器里,貌似协议层没有通知应用层来收这些数据。只到下一次数据的到来。根据上面的描述应该不是因为SO_RCVLOWAT的问题。


在accept时,设置客户端对应socket的非阻塞属性,如下:

       fd = accept(srvsock, (struct sockaddr *)&addr, (socklen_t *)&j);
       if (fd > 0)
       {
           fcntl(fd, F_SETFL, O_NONBLOCK);
           k = setsockopt(fd,SOL_SOCKET,SO_SNDBUF,&maxsndbuf,4);
           if ( k != 0)
......
        }

论坛徽章:
0
2 [报告]
发表于 2007-02-07 09:49 |只看该作者
后来我又些了一个linux的服务端测试程序,客户端采用delphi,发送字符串,服务端将之打出来,情况和上面的一样,代码如下

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int srvsock;
int cltsock[3];
int main(int argc,char* argv[])
{
        int i,k,j,len;
        struct sockaddr_in local;
        fd_set rdfs,wrfs,errfs;
        struct timeval tv;
        struct sockaddr addr;
        char msgbuf[512];


        for(i = 0; i < 3; i++)
                cltsock[i] = 0;
        srvsock = socket(AF_INET, SOCK_STREAM, 0);
        if (srvsock < 0)
        {
                printf("\nsocket error";
                return 1;
        }
        i = fcntl(srvsock, F_SETFL, O_NONBLOCK);
        if (i < 0)
        {
                printf("\nfcntl error";
                close(srvsock);
                return 1;
        }
        k = 1;
        i = setsockopt(srvsock, SOL_SOCKET, SO_REUSEADDR, &k, sizeof(int));
        if (i < 0)
        {
                printf("\nsetsockopt error";
                close(srvsock);
                return 1;
        }
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(5500);
        i = bind(srvsock, (struct sockaddr *)&local, sizeof(local));
        if (i < 0)
        {
                printf("\nbind error";
                close(srvsock);
                return 1;
        }
        i = listen(srvsock, 3);
        if (i < 0)
        {
                printf("\nlisten error";
                close(srvsock);
                return 1;
        }
        for(;
        {
                FD_ZERO(&rdfs);
                FD_ZERO(&errfs);
                FD_SET(srvsock,&rdfs);
                FD_SET(srvsock,&errfs);
                for(i = 0; i < 3; i++)
                        if (cltsock[i] > 0)
                        {
                                FD_SET(cltsock[i],&rdfs);
                                FD_SET(cltsock[i],&errfs);
                        }
                tv.tv_sec = 1;
                tv.tv_usec=0;
                k = srvsock;
                for(i = 0; i < 3; i++)
                        if (cltsock[i] > k) k = cltsock[i];
                i = select(k + 1,&rdfs,NULL,&errfs,&tv);
                if ( i==0) continue;
                if ( i > 0)
                {
                        if (FD_ISSET(srvsock,&rdfs))
                        {
                                j = sizeof(addr);
                                k = accept(srvsock,&addr,&j);
                                if (k > 0)
                                {
                                        if (cltsock[0] > 0) close(cltsock[0]);
                                        cltsock[0] = k;
                                        j = fcntl(k, F_SETFL, O_NONBLOCK);
                                        if (j < 0)
                                        {
                                                printf("\ncltsock fcntl error";
                                                close(k);
                                        }
                                }
                        }
                        if (FD_ISSET(srvsock,&errfs))
                        {
                                printf("\nsrvsock err";
                                close(srvsock);
                                return 1;
                        }
                        if (FD_ISSET(cltsock[0],&rdfs))
                        {
                                memset(msgbuf,0x0,512);
                                len = recv(cltsock[0],msgbuf,512,MSG_NOSIGNAL);
                                if (len == 0)
                                {
                                        close(cltsock[0]);
                                        printf("\nthe peer close";
                                }
                                if (len < 0)
                                {
                                        if ( errno != EAGAIN)
                                        {
                                                close(cltsock[0]);
                                                printf("\nthe peer close due to error";
                                        }
                                }
                                printf("\ndata income,len:%d,content:%s",len,msgbuf);
                        }
                }
        }
        return 0;
}

论坛徽章:
0
3 [报告]
发表于 2007-02-07 09:51 |只看该作者
估计应该是哪个参数没有设置好造成的
只是我不知道而已

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
4 [报告]
发表于 2007-02-07 10:03 |只看该作者
最好采用 head + body 的形式..
TCP有缓存的..不一定每个数据包都立即被应用程序接收到.

论坛徽章:
0
5 [报告]
发表于 2007-02-07 10:13 |只看该作者
刚才我跟了一下2楼的程序
                      memset(msgbuf,0x0,512);
                                len = recv(cltsock[0],msgbuf,512,MSG_NOSIGNAL);
                                if (len == 0)
                                {
                                        close(cltsock[0]);
                                        printf("\nthe peer close";
                                }
                                if (len < 0)
                                {
                                        if ( errno != EAGAIN)
                                        {
                                                close(cltsock[0]);
                                                printf("\nthe peer close due to error";
                                        }
                                }
                                printf("\ndata income,len:%d,content:%s",len,msgbuf);
客户端发送后,服务端实际是收到了的,msgbuf中有正确的内容,只是
                                printf("\ndata income,len:%d,content:%s",len,msgbuf);
一行打出的是上一次收到的东西,比较奇怪。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2007-02-07 10:43 |只看该作者
对了.还有..recv(cltsock[0],msgbuf,512,MSG_NOSIGNAL); 这个MSG_NOSIGNAL标志位..应该是用在send

论坛徽章:
0
7 [报告]
发表于 2007-02-07 11:09 |只看该作者
--

有 nagle 算法

  1. if there is new data to send
  2. if the window size and available data is >= MSS
  3.   send complete MSS size segment now
  4. else
  5.   if there is unconfirmed data still in the pipe
  6.    enqueue data in the buffer until an acknowledge is received
  7.   else
  8.    send data immediately
复制代码


--

论坛徽章:
0
8 [报告]
发表于 2007-02-07 11:55 |只看该作者
设置NODelay看看

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
9 [报告]
发表于 2007-02-07 13:14 |只看该作者
现在的TCP协议栈都做了优化..一般的小包都加了PSH位..所以一般都不会有延时的..我抓我自己的包..都有PSH位.除了那些连续的大数据包.
楼主的情况..是不是客户端的问题..

论坛徽章:
0
10 [报告]
发表于 2007-02-07 13:40 |只看该作者
原帖由 cookis 于 2007-2-7 13:14 发表
现在的TCP协议栈都做了优化..一般的小包都加了PSH位..所以一般都不会有延时的..我抓我自己的包..都有PSH位.除了那些连续的大数据包.
楼主的情况..是不是客户端的问题..



真解,上午抓了一下包,几乎大部分tcp的psh都置位了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP