socket网络编程快速上手(二)——细节问题(2)
2.TCP数据包接收问题
对初学者来说,很多都会认为:客户端与服务器最终的打印数据接收或者发送条数都该是一致的,1000条发送打印,1000条接收打印,长度都为1000。但是,事实上并不是这样,发送打印基本不会有什么问题(只是一般情况,如果发生调度或者其他情况,有可能导致差别,因此也要注意封装),接收打印却不是固定的,下面是测试代码:
测试客户端程序:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> #define PORT 1234
#define MAXDATASIZE 1000 int main(int argc, char *argv[])
{
int sockfd, num;
char buf[MAXDATASIZE + ] = {};
struct sockaddr_in server;
int iCount = ; if (argc != )
{
printf("Usage:%s <IP Address>\n", argv[]);
exit();
} if ((sockfd=socket(AF_INET, SOCK_STREAM, )) == -)
{
printf("socket()error\n");
exit();
}
bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = inet_addr(argv[]);
if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
printf("connect()error\n");
exit();
} while ()
{
memset(buf, , sizeof(buf));
if ((num = recv(sockfd, buf, MAXDATASIZE,)) == -)
{
printf("recv() error\n");
exit();
}
buf[num - ]='\0';
printf("%dth Recv Length: %d\n", iCount++, num);
} close(sockfd); return ;
}
TCP客户端
测试服务器程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> #define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000 int main()
{
int listenfd, connectfd;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t addrlen;
char szbuf[MAXDATASIZE] = {};
int iCount = ;
int iLength = ; if ((listenfd = socket(AF_INET, SOCK_STREAM, )) == -)
{
perror("Creating socket failed.");
exit();
} int opt = SO_REUSEADDR;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); bzero(&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -)
{
perror("Bind()error.");
exit();
}
if (listen(listenfd, BACKLOG) == -)
{
perror("listen()error\n");
exit();
} addrlen = sizeof(client);
if ((connectfd = accept(listenfd, (struct sockaddr*)&client, &addrlen)) == -)
{
perror("accept()error\n");
exit();
}
printf("You got a connection from cient's ip is %s, prot is %d\n", inet_ntoa(client.sin_addr), htons(client.sin_port)); memset(szbuf, 'a', sizeof(szbuf));
while (iCount < )
{
iLength = send(connectfd, szbuf, sizeof(szbuf), );
printf("%dth Server Send Length %d\n", iCount++, iLength);
} printf("send over!\n");
sleep(); close(connectfd);
close(listenfd); return ;
}
TCP服务器程序
客户端接收打印片段如下:
936th Recv Length:
937th Recv Length:
938th Recv Length:
939th Recv Length:
940th Recv Length:
941th Recv Length:
942th Recv Length:
943th Recv Length:
944th Recv Length:
945th Recv Length:
946th Recv Length:
947th Recv Length:
948th Recv Length:
949th Recv Length:
950th Recv Length:
951th Recv Length:
952th Recv Length:
953th Recv Length:
954th Recv Length:
955th Recv Length:
956th Recv Length:
957th Recv Length:
958th Recv Length:
959th Recv Length:
960th Recv Length:
961th Recv Length:
962th Recv Length:
963th Recv Length:
964th Recv Length:
965th Recv Length:
966th Recv Length:
967th Recv Length:
968th Recv Length:
969th Recv Length:
970th Recv Length:
971th Recv Length:
972th Recv Length:
973th Recv Length:
974th Recv Length:
975th Recv Length:
976th Recv Length:
977th Recv Length:
978th Recv Length:
979th Recv Length:
980th Recv Length:
981th Recv Length:
982th Recv Length:
983th Recv Length:
984th Recv Length:
985th Recv Length:
986th Recv Length:
987th Recv Length:
988th Recv Length:
989th Recv Length:
990th Recv Length:
991th Recv Length:
992th Recv Length:
993th Recv Length:
994th Recv Length:
995th Recv Length:
996th Recv Length:
997th Recv Length:
998th Recv Length:
999th Recv Length:
1000th Recv Length:
1001th Recv Length:
1002th Recv Length:
1003th Recv Length:
1004th Recv Length:
1005th Recv Length:
1006th Recv Length:
1007th Recv Length:
1008th Recv Length:
1009th Recv Length:
1010th Recv Length:
1011th Recv Length:
1012th Recv Length:
1013th Recv Length:
1014th Recv Length:
1015th Recv Length:
1016th Recv Length:
1017th Recv Length:
1018th Recv Length:
1019th Recv Length:
1020th Recv Length:
1021th Recv Length:
1022th Recv Length:
1023th Recv Length:
1024th Recv Length:
1025th Recv Length:
1026th Recv Length:
1027th Recv Length:
1028th Recv Length:
1029th Recv Length:
1030th Recv Length:
1031th Recv Length:
1032th Recv Length:
1033th Recv Length:
1034th Recv Length:
1035th Recv Length:
1036th Recv Length:
1037th Recv Length:
1038th Recv Length:
1039th Recv Length:
1040th Recv Length:
1041th Recv Length:
1042th Recv Length:
1043th Recv Length:
1044th Recv Length:
1045th Recv Length:
1046th Recv Length:
1047th Recv Length:
1048th Recv Length:
1049th Recv Length:
1050th Recv Length:
客户端接收打印片段
服务器发送打印片段整理时发现丢失了,大家可以自己试试,没有问题。
不难发现,服务器发送正常,客户端在接收时却和我们想的很不一样,但发送和接收的总数据量是一致的,就是说数据没有丢失。如果编程者认为TCP情况下发送和接收的数据长度都一致的,那就极有可能在代码中体现出这一思想,最终出现问题。
其实,这就是所谓的“粘包”现象,Stevens很明确地已经指出了这一点,他说,“UDP是长度固定的、无连接的不可靠报文传输;TCP是有序、可靠、双向的面向连接字节流”。他没说TCP是长度固定的,有没有?当然我更倾向于这样的理解,UDP是面向报文的,报文在传输时是不能被分割的(只是从应用层来看);TCP是面向字节流的,接收多少数据完全取决于发送和接收的速度了,有多少数据recv就返回多少,数据长度并不和send保持一致,也没这个必要。
那么这个问题怎么解决呢?其实,我们只要将recv封装一层就可以了,那就是我们熟悉的readn函数(该函数不是系统调用),代码如下:
int readn(int connfd, void *vptr, int n)
{
int nleft;
int nread;
char *ptr;
struct timeval select_timeout;
fd_set rset; ptr = vptr;
nleft = n; while (nleft > )
{
FD_ZERO(&rset);
FD_SET(connfd, &rset);
select_timeout.tv_sec = ;
select_timeout.tv_usec = ;
if (select(connfd+, &rset, NULL, NULL, &select_timeout) <= )
{
return -;
}
if ((nread = recv(connfd, ptr, nleft, )) < )
{
if(errno == EINTR)
{
nread = ;
}
else
{
return -;
}
}
else if (nread == )
{
break;
}
nleft -= nread;
ptr += nread;
}
return(n - nleft);
}
readn
相应的也有writen函数
int writen(int connfd, void *vptr, size_t n)
{
int nleft, nwritten;
char *ptr; ptr = vptr;
nleft = n; while(nleft>)
{
if((nwritten = send(connfd, ptr, nleft, )) == ERROR)
{
if(errnoGet() == EINTR)
{
//PRT_ERR(("EINTR\n"));
nwritten = ;
}
else
{
//PRT_ERR(("Send() error, 0x%x\n", errnoGet()));
return ERROR;
}
}
nleft -= nwritten;
ptr += nwritten;
} return(n);
}
writen
函数中为什么对EINTR进行处理后面再说,也是必不可少的。
在处理TCP发送和接收部分时,可以说必须要使用上述封装,否则等到造成数据不完整或者不一致后再去找问题,可能就麻烦了。这个是必不可少滴。
socket网络编程快速上手(二)——细节问题(2)的更多相关文章
- socket网络编程快速上手(二)——细节问题(5)(完结篇)
6.Connect的使用方式 前面提到,connect发生EINTR错误时,是不能重新启动的.那怎么办呢,是关闭套接字还是直接退出进程呢?如果EINTR前,三次握手已经发起,我们当然希望链路就此已经建 ...
- socket网络编程快速上手(二)——细节问题(4)
5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号的东西,感觉他就像一位隐士,很少遇到他,而他又无处不在 ...
- socket网络编程快速上手(二)——细节问题(1)
三.细节问题一个也不能少 Socket编程说简单也简单,程序很容易就能跑起来,说麻烦还真是麻烦,程序动不动就出问题.记得刚开始写网络代码的时候,那真是令人抓狂的经历,问题一个套一个,一会服务器起不来了 ...
- socket网络编程快速上手(一)
工作以来,写了很多socket相关的代码.磕磕碰碰,走了很多弯路,也积累了一些东西,今天正好整理一下.为了证明不是从书上抄来的,逻辑会有点乱(借口,呵呵)!知识点的介绍也不会像书上说的那么详细和精准, ...
- socket网络编程快速上手(二)——细节问题(3)
3.SIGPIPE问题 人怕牺牲,我们写的程序也一样,人有死不瞑目,程序又何尝不是?程序跑着跑着,突然就崩掉了.好一点的牺牲前告诉你些打印,差点的也能用core文件等一些手段查出死在哪了,最惨不忍睹的 ...
- Java网络编程快速上手(SE基础)
参考资料:百度百科TCP协议 本文涉及Java IO流.异常的知识,可参考我的另外的博客 一文简述Java IO 一文简述JAVA内部类和异常 1.概述 计算机网络相关知识: OSI七层模型 一个报文 ...
- SOCKET网络编程细节问题(4)
SOCKET网络编程快速上手(二)——细节问题(4) 5.慢系统调用及EINTR 还记得前面readn和writen函数么?里面有个EINTR,现在就来谈谈这个,这个很重要. Linux世界有个叫信号 ...
- SOCKET网络编程细节问题3
SOCKET网络编程快速上手(二)——细节问题(3) 3.SIGPIPE问题 人怕牺牲,我们写的程序也一样,人有死不瞑目,程序又何尝不是?程序跑着跑着,突然就崩掉了.好一点的牺牲前告诉你些打印,差点的 ...
- SOCKET网络编程细节问题(2)
SOCKET网络编程快速上手(二)——细节问题(2) 2.TCP数据包接收问题 对初学者来说,很多都会认为:客户端与服务器最终的打印数据接收或者发送条数都该是一致的,1000条发送打印,1000条接收 ...
随机推荐
- Dom中的nodeName、nodeValue 、nodeType
nodeName.nodeValue 以及 nodeType 包含有关于节点的信息. nodeName 属性含有某个节点的名称. 元素节点的 nodeName 是标签名称 属性节点的 nodeName ...
- Struts2.0+Spring3+Hibernate3(SSH~Demo)
Struts2.0+Spring3+Hibernate3(SSH~Demo) 前言:整理一些集成框架,发现网上都是一些半成品,都是共享一部分出来(确实让人很纠结),这是整理了一份SSH的测试案例,完全 ...
- SQL2005性能分析一些细节功能你是否有用到?(二)
原文:SQL2005性能分析一些细节功能你是否有用到?(二) 上一篇:SQL2005性能分析一些细节功能你是否有用到? 我简单的提到了些关于SQL性能分析最基本的一些方法,下面的文章我会陆续补充.前面 ...
- The Swift Programming Language-官方教程精译Swift(9) 枚举-- --Enumerations
枚举定义了一个通用类型的一组相关的值,使你可以在你的代码中以一个安全的方式来使用这些值. 如果你熟悉 C 语言,你就会知道,在 C 语言中枚举指定相关名称为一组整型值.Swift 中的枚举更加灵活 ...
- 在SQL Server中对视图进行增删改
原文:在SQL Server中对视图进行增删改 Lesktop开源IM发布以后,有一些网友问及如何在嵌入IM后与自己网站的用户系统整合(即如何让嵌入的IM直接使用网站原有的用户数据库,而不需要将已有的 ...
- Oracle在rownum使用结果集排序
Oracle在rownum使用结果集排序 对于 Oracle 的 rownum 问题,非常多资料都说不支持>,>=,=,between...and,仅仅能用以上符号(<.< ...
- JSLint是一个JavaScript的代码质量工具
JSLint是一个JavaScript的代码质量工具 可能都或多或少的知道JSLint是一个JavaScript的代码质量工具,一个JavaScript语法检查器和校验器,它能分析JavaScript ...
- JDK-windows7环境变量配置-亲测版本 以及HelloWorld
1.下载并安装jdk,假设安装1.6.0_45版本到C:\Program Files\Java,则安装完毕后,目录结构为: C:\PROGRAM FILES\JAVA├─jdk1.6.0_45│ ├─ ...
- SQL点滴22—性能优化没有那么神秘
原文:SQL点滴22-性能优化没有那么神秘 经常听说SQL Server最难的部分是性能优化,不禁让人感到优化这个工作很神秘,这种事情只有高手才能做.很早的时候我在网上看到一位高手写的博客,介绍了SQ ...
- C语言身份证信息查询系统(修改版)
很久以前写了一个<C语言身份证信息查询系统>,如果你点击链接进去看了. 估计也会被我那磅礴大气的代码震惊到的,最近复习/学习文件操作,把代码改了改,算是对以前还不会文件操作的时候的愿望,哈 ...