免费注册 查看新帖 |

Chinaunix

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

[C] linux socket接收未知长度的字符串问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-07-18 11:20 |只看该作者 |倒序浏览
情景:客户端通过socket向服务器发送 开始标志0x02+命令+数据 +结束标志0x03
例如:2NEW|20130716001|M3
主要问题是:客户端发送的数据长度是未知的,我现在的程序一次能接收的数据长度是定义好的,比如512,1024。我想做到结束不定长度的数据,检测到结束标志0x03的时候才结束。

我现在的程序是阻塞的。请高手帮我看下,下面是我现在的程序,需要怎么修改才可以实现我说的功能,给个思路也可以。

main.c
  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include "socket.h"

  4. int main(int argc, char *argv[])
  5. {
  6.         pthread_t t;
  7.           pthread_create(&t,NULL,mysocket,NULL);   //socket
  8.          
  9.           while(1)
  10.           {}

  11.         return 0;
  12. }
复制代码
socket.c
  1. #include "socket.h"
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/socket.h>       /*  socket definitions        */
  6. #include <sys/types.h>        /*  socket types              */
  7. #include <arpa/inet.h>        /*  inet (3) funtions         */
  8. #include <unistd.h>           /*  misc. UNIX functions      */

  9. /*******************************************************
  10. *  函数名称:mysocket
  11. *  参数列表:void* args
  12. *  返回值:  NULL
  13. *  函数功能:socket线程函数
  14. *******************************************************/
  15. void* mysocket(void* args)
  16. {
  17.              unsigned char buf[MAX_LINE];
  18.              unsigned char addr_p[INET_ADDRSTRLEN];
  19.              unsigned int  n,i,ret;

  20.         int       list_s;                /*  listening socket          */
  21.           int       conn_s;                /*  connection socket         */
  22.              socklen_t len;
  23.             short int port;                  /*  port number               */
  24.             struct    sockaddr_in servaddr;  /*  socket address structure  */
  25.         struct    sockaddr_in clenaddr;  /*  socket address structure  */
  26.         /*清空结构体*/
  27.         memset(&servaddr,0,sizeof(servaddr));
  28.         /*  Set all bytes in socket address structure to
  29.                 zero, and fill in the relevant data members   */       
  30.         memset(&servaddr, 0, sizeof(servaddr));
  31.         servaddr.sin_family      = AF_INET;
  32.         servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  33.         servaddr.sin_port        = htons(MYPORT);

  34.         /*  Create the listening socket  */
  35.         if ( (list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
  36.                 perror("create socket error:");
  37.         }
  38.         /*允许地址立即重用*/
  39.         unsigned int bReuseaddr =  0x01;
  40.         setsockopt(list_s,SOL_SOCKET,SO_REUSEADDR,(void *)&bReuseaddr,sizeof(bReuseaddr));
  41.         /*  Bind our socket addresss to the
  42.             listening socket, and call listen()  */
  43.              if( bind(list_s, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
  44.                 perror("bind socket error:");
  45.         }

  46.         if ( listen(list_s, 10) < 0 ) {
  47.                 perror("listen socket error:");
  48.            }  
  49.              printf("waiting ...\n");

  50.         while(1)
  51.         {
  52.                 len = sizeof(struct sockaddr_in);
  53.                 /*  Wait for a connection, then accept() it  */
  54.                 conn_s = accept(list_s, (struct sockaddr *) &clenaddr, &len);
  55.                 if(conn_s < 0) {
  56.                         perror("accept socket error:");
  57.                 }

  58.                 memset( buf, '\0', MAX_LINE );
  59.                 n = read(conn_s, buf, MAX_LINE);

  60.                 inet_ntop(AF_INET, &clenaddr.sin_addr, addr_p, sizeof(addr_p));
  61.                 printf("client IP is %s, port is %d\n", addr_p, ntohs(servaddr.sin_port));

  62.                 printf("read n = %d \n",n);

  63.                 if(n == 0){
  64.                         printf("no message! \n");
  65.                         close(conn_s);
  66.                         break;
  67.                 }
  68.                 printf("%s\n",buf);

  69.                 close(conn_s);
  70.         }

  71.         if(close(list_s) == -1){
  72.                 perror("fail to close");
  73.                 exit(1);
  74.         }

  75.         return NULL;
  76. }
复制代码
socket.h
  1. #ifndef _SOCKET_H
  2. #define _SOCKET_H

  3. #define MAX_LINE 8
  4. #define MYPORT 8000

  5. void* mysocket(void* args);

  6. #endif
复制代码
一次接受的最大数据设为8 是为了测试方便。

客户端:
client.c
  1. #include <stdio.h>
  2. #include <sys/socket.h>
  3. #include <arpa/inet.h>
  4. #include <string.h>
  5. #include <stdlib.h>

  6. #define DEST_IP "127.0.0.1"
  7. #define DEST_PORT 8000

  8. int main(int argc, char *argv[])
  9. {
  10.      struct sockaddr_in sin;
  11.      int sfd,n,i;
  12.      char *str = "1234567890";

  13.      bzero( &sin, sizeof( sin ) );
  14.      sin.sin_family = AF_INET;
  15.      sin.sin_addr.s_addr = inet_addr( DEST_IP );
  16.      sin.sin_port = htons( DEST_PORT );

  17.      sfd = socket( AF_INET, SOCK_STREAM, 0 );
  18.      connect( sfd, (struct sockaddr *)&sin, sizeof(sin));
  19.      write( sfd, str, strlen( str ) );

  20.      close(sfd);

  21.      return 0;
  22. }

复制代码
客户端发送这样的数据,服务器只能接收到“12345678”。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
2 [报告]
发表于 2013-07-18 13:01 |只看该作者
整個邏輯分成這麼幾層:網絡層只管收數據放到一塊內存裏,然後協議解析層負責解析內存裏的數據得到一個個的數據包,最後應用層處理一個個的數據包就可一了。一般的二進制協議設計會在數據包開頭加入整個數據包的長度,這樣解析的時候讀完指定長度的數據就是一個包了。 文本協議可能就是樓主原來實現的這種靠特殊字符或者字符串來標誌數據包的結束。

论坛徽章:
0
3 [报告]
发表于 2013-07-18 13:57 |只看该作者
tcp本身是流式的,你尽可能地收,收到了之后,再从字符数组里找0x03,每找到一个,就提取出来。如果最后几个字符没找到,就把它们做为包头,下一次再接收到数据后,把收到的数据附在之后就可以了。

论坛徽章:
0
4 [报告]
发表于 2013-07-18 14:53 |只看该作者
回复 2# csumck


    我看了很多网络上的例子,包括使用select实现非阻塞模式的,都是有设定一次接收的最大字符数。

    我现在的代码不是只read了一次么,我本来想写一个循环去不断的read,直到从接收的数据中找到0x03才结束read,重新等待。

    但是代码没有写出来。在read的地方修改:

    while( buf[n-1] != 0x03 )       //判断接收到的数据的最后一位
    {
            n = read(conn_s, buf, MAX_LINE);            //读取客户端数据
            strcat( savebuf, buf );             //将接收到的数据添加到savebuf中,这里也没有办法确定savebuf的大小,所以应该根据 n 的大小动态申请内存
     }

     这段代码不行的,是不是不能在while(1)中再嵌套while(1)啊

论坛徽章:
0
5 [报告]
发表于 2013-07-18 16:14 |只看该作者
while( buf[n-1] != 0x03 )
这有个前提,每次read,正好把发送的这一条全接下来,不多也不少。但上面说了,TCP是流式的,不可能实现的。UDP没问题。
如果你while,也可以,每次读一个字符:n = read(conn_s, buf, 1);  然后看这个字符是不是0x03。不要认为这样慢, 很多轻量级的http server都是这么读的。

如果一次读多字符,就一直读到n<=0,然后再回过去来处理字符串。这时savebuf里面肯定是有多个0x03,依次取,留下一点尾巴就是下一包的前半段。

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
6 [报告]
发表于 2013-07-18 17:30 |只看该作者
回复 4# dolphin836


    給你寫個僞代碼:
while(讀取網絡數據到內存buf中)//網絡層
{
     while(在buf中查找0x03)//協議解析層
     {
          將0x03之前的數據作爲數據包取出來處理; //應用層
     }
}

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
7 [报告]
发表于 2013-07-18 17:30 |只看该作者
不够长就先攒着啊.

论坛徽章:
0
8 [报告]
发表于 2013-07-18 22:12 |只看该作者
先分析一下客户端发送数据的需求。
1.1为什么会出现不定长数据?
1.2 这些不定长数据的长度的统计信息--期望找到最大值

论坛徽章:
4
双子座
日期:2014-08-28 10:08:002015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:58:112015年亚洲杯之阿联酋
日期:2015-03-13 03:25:15
9 [报告]
发表于 2013-07-18 22:49 |只看该作者
思路不对,从来不要做一个接收未知长度的socket。

论坛徽章:
0
10 [报告]
发表于 2013-07-19 09:09 |只看该作者
这个程序是在一个病人信息管理系统中,例如锁定病人,客户端就要发送病人的studyid到服务器,但是一次选择锁定多少病人是不定的,
所以一次会发送的studyid个数也不定,所以数据包的长度也就不定了。我只收到了这样的协议,至于修改协议会很麻烦,要开会。
你觉得这样的协议合理么?是否存在弊端?是否需要设定最大允许的个数?还有就是,是否在数据包开始附加字符包长度更好?
回复 8# buzs


   
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP