这几天在学习TCP/IP Sockets in C(2nd) Practical Guide for Programmers,在学到如何确认Client和Server之间的数据传输时是一段数据的begin和end时,作者讲了两种方法:
第一种:每次传固定长度的数据,当然多长你要在Client和Server之间协调好。
第二种:规定begin和end的token-标记,(当然你传的数据要是出现你规定的token,也是有办法解决的,不是本文要说的)
好了,问题出来了,如何识别token来把token头和token尾之间真正的数据拿出来呢?
作者用到了本文的主角——strtok函数,翻《C程序设计语言》(我也想man的,可是deepin没装,奇怪~~~)看看如何说:
不晓得大家看不看得懂,反正我没咋看懂,不过应该能把token之间的数据分出来~~~
整理如下:
strtok Sample Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/* strtok example */
#include <stdio.h> #include <string.h> char *strtok_bsd( char *s, const char *delim); char *strtok_win( char *s, const char *delim); int main () { char str[] = "- This, a sample string"; char *pch; printf ( "Splitting string \"%s\" into tokens:\n", str); pch = strtok (str, " ,.-"); while (pch != NULL) { printf ( "%s\n", pch); pch = strtok ( NULL, " ,.-"); } return 0; } |
strtok_bsd Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//在NetBSD中strtok的实现: char *strtok_r_bsd( char *s, const char *delim) { const char *spanp; //span表示分隔,p表示指针 char c, sc; //c表示char字符,sc表示 span char char *tok; //token表示分隔的段 static char *last; //把last设置为一个静态局部变量来保存余下内容的地址。 //当开始结尾都为NULL的时候,说明没有字符被查找,所以返回NULL if (s == NULL && (s = *last) == NULL) return ( NULL); //由goto组成的循环是在扫描字符串的时候,当遇到所需要匹配的字符时,略过这个字符。 cont: c = *s++; for (spanp = delim; (sc = *spanp++) != 0; ) { if (c == sc) goto cont; } //下一个字符为0,则表示到达了搜索结果,把last置为NULL,并返回NULL if (c == 0) { *last = NULL; return ( NULL); } //把原始的字符串指针回退。 tok = s - 1; //开始扫描字符串中是否含有要匹配的字符,之后把这个匹配字符之前的部分返回。 //这看似是个无限循环,但当源字符串和匹配字符串都走到结尾时,也就是s和sc都为NULL时,最外层循环最后会走到return(tok)结束循环。 for (;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[- 1] = 0; *last = s; return (tok); } } while (sc != 0); } } |
strtok_win Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//在Microsoft中strtok的实现: char *strtok_win( char *s, const char *delim) { static unsigned char *last; //保存分隔后剩余的部分 unsigned char *str; //返回的字符串 const unsigned char *ctrl = ( const unsigned char *)delim; //分隔字符 //把分隔字符放到一个索引表中。定义32是因为ASCII字符表最多是0~255个,也是说用最大的255右移3位,也就是除以8一定会是32中的一个数。 unsigned char map[ 32]; int count; //把map全部清为0,之后相与的操作,与0的都为0 for (count = 0; count < 32; count++) map[count] = 0; //把匹配字符放入表中 //放入的算法是把匹配字符右移3位,相当于除以8,的数值 并上(加上) //匹配字符与7,得到低3位,得出的结果,是把1左移的位数。最大左移位数是7,也就是所表示的最大值是128, do { map[*ctrl >> 3] |= ( 1 << (*ctrl & 7)); } while (*ctrl++); //原始字符串是否为空,如果为空表示第二次获取剩余字符的分隔部分。 if (s) str = ( unsigned char *)s; else str = last; //在表中查找是否有匹配的字符,如果有略过 while ((map[*str >> 3] & ( 1 << (*str & 7))) && *str) str++; //重置需要扫描的字符串 s = ( char *)str; //开始扫描 for ( ; *str; str++) { if ( map[*str >> 3] & ( 1 << (*str & 7))) { *str++ = '\0'; //当找到时,把匹配字符填为0,并且把str指向下一位。 break; //退出循环 } } last = str; // 把剩余字符串的指针保存到静态变量last中。 if (s == ( char *)str) return NULL; //没有找到,也就是没有移动指针的位置,返回NULL else return s; //找到了,返回之前字符串的头指针 } |
ps:将strtok换成strtol_bsd和strtok_win就ok了,再单步调试看看~~~
NetBSD的方法是节约了空间,牺牲了时间(它的时间复杂度为N2),而微软的方法是节约了时间(它的时间复杂度为N),牺牲了空间(开了一个32个8位的空间。