随笔五年前(一)

  1. 为了减少包得大小,当传得包大小不一定的时候,如一个可变的url的时候,这个url一定要定义在结构体的最后面
struct mypacket
{
    hash[40];
    char url[1024];
};

计算大小的时候传送数据大小为len = 40 + strlen[url] + 1;
如果结构体中的url定义在hash的前面,那么当len大小为60(eg)的时候,只发送了char url这个数组的前60个,并没有传送hash.

2. bind: Socket operation on non-socket
可能不是bind这的问题,是前面sockfd的问题,注意优先级,赋值语句优先级最低的~

if((sockfd = socket(AF_INET,SOCK_DGRAM,0))==-1) //这句要注意括号的位置,如果((sockfd = socket(AF_INET,SOCK_DGRAM,0)==-1)) 结果sockfd为0,为soket返回值是否==-1的判断值。
{
cout<<"socket"<<endl;
return -1;
}
if( bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1)
{
perror("bind");
close(sockfd);
return -1;
}


3. 一片有争议的堆栈文章
glibc/pthread按照ulimit -s的设置 在线程创建之初就为线程的栈实际分配了这些内存空间
附:另外线程函数必须调用pthread_exit()结束 否则直到主进程函数退出才释放(it.paiming.org/bbs/simple/index.php?t9367.html)
pthread_create的man-page上说若在程序启动的时刻 ulimit -s是unlimited那么x86-32默认线程栈为2MiB否则 按照ulimit -s指定的大小分配(kernel.org/doc/man-pages/online/pages/man3/pthread_create.3.html)
p.s. 普通用户ulimit的时候 只能减小数值而不是增大
p.p.s. 限制最大stack size据说 是为了防止无限递归调用以及无限创建新线程/进程(doubt for forking processes)

4. 遗留问题:linux文件句柄
limit -n 显示的是linux下一个进程所打开文件句柄的最大数,包括socket,和包括默认的三个标准输入输出标准错误输出。还有就是使用动态库会使用一个文件句柄。
写程序时要用到别人的一个库,而这个库有个bug,中科院计算机所的libICTCLAS50.a。调用一个函数时候,这个函数每次运行打开配置文件后文件句柄竟然没有释放。
程序要求后台,服务,多线程,就是一直在后台多线程的运行,所以可能要重复的使用库中的一个函数,而每次使用后都会增加一个文件句柄,一直达到上限后,程序无法打开配置文件,因为已经到达最大值,异常结束。我想不损失性能,重复调用这个函数。
1. 性能不考虑,可以每次运行时候都重新启一个线程。
2.如果要用线程池的话因为每个线程都要重复的利用,所以我想用类封装下这个对象,分配一个栈上的对象,通过对象调用库里面的函数。这样在超出对象的作用域,对象资源自动回收,但是还是提示不能打开文件。可能这个句柄是在堆上的,那么这样报错还可以理解。
3.我又随便尝试了下分配了一个堆上的对象,用对象指针调用库里面的函数,竟然没有报错!?

5. 一个调试了好长时间的错误。
在头文件common .h定义的struct,改了以后莫名奇妙的报错。
原因。make的问题,当生成.o链接文件后其他文件包含common.h的结构体,但是在其他.o文件中并不会重新编译,所以出现结构体不一致的情况!

6. TCP 粘包现象及解决方法
一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系。接收端接收到正确的后都要给发送端一个应答。不给应答的算超时,发送端将重发。 出现粘包和半包现象,是因为TCP当中,只有流的概念,没有包的概念.。
可以使用UDP协议.这样可以就可以区分每个包了.但是要确保包的丢失处理.为了提到效率,可以考虑写一个滑动窗口进行收发包.
若采用TCP协议进行传输,就要将每个包区分开来.可以有三种方式.因为TCP是面向流的.流只有打开和关闭,你要用一个流传输多个包,那就要向办法区分出每个包.
1.可以每次发送同样大小的包,过大的包不予发送,过小的包,后面部分用固定的字符’\0’进行填充。
2. 将流按字符处理,抽出一个字符做转义字符(通常Java用’\’来做转义字符,比如”\n”表示换行).假如就设’\’为转义字符,发送方如果流当中出现’\’,就在后面在追加一个’\’,如果包结束,则用’\’做包的结束符.这样,在接收方,若读取一个单独的’\’或者流结束,就标示前面的内容构成一个包,如果连续读取两个’\’,就将两个’\’用一个’\’进行替换.这样,就可以保证原来包中的信息不变,同时也能区分出每个包了。
3.在发送方发送一个包的时候,先将这个包的长度发送给对方(一般是4个字节表示包长),然后再将包的内容发送过去.接收方先接收4个字节,看看包的长度,然后按照长度来接收包,最好是大端字节序。
以上三种方法,是网络传输中经常用到的方法.后两种很常见.最后一种,在TCP长连接传输中应用最多.
综合以上的说法,就是要在TCP协议以上再封装一层协议,用来做分包的信息交换.
当然,如果TCP不是非要长连接,或者,信息包不是批量传输的情况下.可以一次TCP连接只传输一个包.这种情况下一般,一次TCP完成一次交互,即发送方发送信息包,接收方接收信息包同时发送一个接收方的响应包给发送方,表明接收方收到信息包,还是收到了错误包,或者接收方系统异常没有处理这个包之类的信息. 其实HTTP的交互过程,就是这样的.

8. 内存映像
内存映像其实就是在内存中创建一个和外存文件完全相同的映像。用户可以将整个文件映射到内存中也可以部分映射到内存。系统会将对内存映像的改动如实的反映到外存文件中。从而实现了通过内存映像对外存文件的操作。

内存映像的特点:
1、 可以加快对IO的操作速度。
2、 用户可以通过指针对文件进行操作,间接~~~
3、 实现了文件数据的共享,将外存文件映射到共享内存中,很方便的实现了数据共享,并能完成把数据保存到外存的工作。

注:内存映像只能对内部可以定位的文件进行操作,如普通文件。不能对管道,套接字文件进行操作。

9. 双重指针的传参
有函数
void fun(char **p);
传参数时一定传指针的地址
char *p1 = NULL;
fun(char &p);
不能
char **p2 = NULL;
fun(p2);
传得仍然是一个指针的拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值