标签:
=============================测试代码===========================
客户端:
#include<winsock2.h> //包含头文件
#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"WS2_32.lib") //显式连接套接字库
int main() //主函数开始
{
WSADATA data; //定义WSADATA结构体对象
WORD w=MAKEWORD(2,0); //定义版本号码
::WSAStartup(w,&data); //初始化套接字库
SOCKET s; //定义连接套接字句柄
char sztext[20]={0};
s=::socket(AF_INET,SOCK_STREAM,0); //创建TCP套接字
sockaddr_in addr; //定义套接字地址结构
addr.sin_family=AF_INET; //初始化地址结构
addr.sin_port=htons(75);
addr.sin_addr.S_un.S_addr=inet_addr("192.168.0.20");
printf("客户端已经启动\r\n"); //输出提示信息
int ret_con = ::connect(s,(sockaddr*)&addr,sizeof(addr));
Sleep(100000);
char t[200]="12345";
int r = ::send(s,t,0,0);
Sleep(1000);
::closesocket(s); //关闭套接字句柄
char buf[100]={0};
int r2 = ::recv(s,buf,100,0);
printf("%s",buf);
::WSACleanup(); //释放套接字库
if(getchar()) //如果有输入,则关闭程序
{
return 0; //正常结束程序
}
else
{
::Sleep(100); //程序睡眠
}
return 0;
}
服务器:
#include<winsock2.h> //包含头文件
#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"WS2_32.lib") //显式连接套接字库
int main() //主函数开始
{
WSADATA data; //定义WSADATA结构体对象
WORD w=MAKEWORD(2,0); //定义版本号码
char sztext[]="你已经连接上"; //定义并初始化发送到客户端的字符数组
::WSAStartup(w,&data); //初始化套接字库
SOCKET s,s1; //定义连接套接字和数据收发套接字句柄
s=::socket(AF_INET,SOCK_STREAM,0); //创建TCP套接字
sockaddr_in addr,addr2; //定义套接字地址结构
int n=sizeof(addr2); //获取套接字地址结构大小
addr.sin_family=AF_INET; //初始化地址结构
addr.sin_port=htons(75);
addr.sin_addr.S_un.S_addr=INADDR_ANY;
::bind(s,(sockaddr*)&addr,sizeof(addr)); //绑定套接字
::listen(s,5); //监听套接字
printf("服务器已经启动\r\n"); //输出提示信息
while(true)
{
s1=::accept(s,(sockaddr*)&addr2,&n); //接受连接请求,正常情况下此函数是个阻塞函数,如果没有阻塞则是此函数运行失败
if(s1!=NULL)
{
printf("%s已经连接上\r\n",inet_ntoa(addr2.sin_addr));
//::send(s1,sztext,sizeof(sztext),0); //向客户端发送字符数组
}
char buf[100]="";
::recv(s1,buf,100,0);
printf("%s",buf);
Sleep(4*1000);
char t[200]="12345";
int r = ::send(s1,t,5,0);
printf("\n%d\n",r);
Sleep(10*1000);
::closesocket(s1);
Sleep(500*60*1000);
::closesocket(s); //关闭套接字句柄
::WSACleanup(); //释放套接字库
if(getchar()) //如果有输入,则关闭程序
{
return 0; //正常结束程序
}
else
{
::Sleep(100); //应用睡眠0.1秒
}
}
}
======================================================================
测试环境:
本机: WIN7 192.168.0.20
虚拟机: WINXP 192.168.0.250
抓包工具: Wireshark
测试内容:
1.客户端连接IP地址不存在或者端口没有开放
IP不存在时Wireshark抓不到数据包
使用下面的代码测试connect返回时间
int t1 = clock();
int ret_con = ::connect(s,(sockaddr*)&addr,sizeof(addr));
int t2 = clock() - t1;
测试结果是20秒
端口不存在时,可以抓到6个数据包
客户端尝试连接3次失败之后不再尝试连接
Connect函数返回时间是1秒
2.正常三次握手的抓包情况
成功抓到三次握手数据包。Connect返回时间为0
3.研究三次握手与accept函数有无关系
研究方法,服务器accept函数上面下断点,单步走,查看抓包情况
accept函数还没有调用已经抓到数据包,实验证明三次握手与accept函数无关
4.客户端简单发送一个数据包
服务器代码:(connect函数之后就一直sleep)
s1=::accept(s,(sockaddr*)&addr2,&n); //接受连接请求,正常情况下此函数是个阻塞函数,如果没有阻塞则是此函数运行失败
if(s1!=NULL)
{
printf("%s已经连接上\r\n",inet_ntoa(addr2.sin_addr));
}
Sleep(1000 * 1000);
一共五条数据,前三条是3次握手,第四条是send发送”12345”的这条数据包,第5条是服务器响应数据包,表示自己已经收到数据了。测试过程中服务器并没来得及调用recv函数,说明TCP协议把数据先接收到了某个缓冲区,等待应用程序来读取。
5.客户端send发送数据为0的情况
服务器代码:
s1=::accept(s,(sockaddr*)&addr2,&n); //接受连接请求,正常情况下此函数是个阻塞函数,如果没有阻塞则是此函数运行失败
if(s1!=NULL)
{
printf("%s已经连接上\r\n",inet_ntoa(addr2.sin_addr));
}
char buf[100]="";
::recv(s1,buf,100,0);
printf("接收到的数据为:%s",buf);
Sleep(400*1000);
客户端代码:
char t[200]="12345";
int r = ::send(s,t,0,0);
客户端方面没有抓到发送出去的数据包,只有握手数据包
服务器方面也没有收到数据包
调用closesocket函数之后客户端给服务发送了一个数据包,服务器发送了响应数据包
由于接收到客户端的closesocket函数,服务器的recv函数立即返回。
6.客户端send函数之后立即调用closesocket函数
直接运行:
可以看到:服务器方面没有响应客户端的send函数(发送相应的ACK数据包)而是直接响应了closesocket数据包。
但不运行:
数据包正常,分析原因应该是TCP的规定,接收到closesocket函数之后不再响应send的数据包
7.客户端连续调用send函数
服务器是否调用recv进行接受,客户端连续发送的数据会分两拨到达
服务器接受缓冲区够大的情况下
8.抓取四次握手释放连接数据包
服务器代码:
s1=::accept(s,(sockaddr*)&addr2,&n); //接受连接请求,正常情况下此函数是个阻塞函数,如果没有阻塞则是此函数运行失败
if(s1!=NULL)
{
printf("%s已经连接上\r\n",inet_ntoa(addr2.sin_addr));
}
Sleep(2*1000);
::closesocket(s1);
客户端代码:
int ret_con = ::connect(s,(sockaddr*)&addr,sizeof(addr));
Sleep(1*1000);
::closesocket(s); //关闭套接字句柄
Sleep(100*1000);
要想抓到四次握手数据包必须一方先close函数,另一方紧接着close函数才可以
9.客户端closesocket之后服务器端一直sleep(不发送相应closesocket)
可以看到服务器端始终没有调用closesocket,120秒之后客户端采取另一个策略,发送RST数据包
标签:
原文地址:http://blog.csdn.net/liujiayu2/article/details/46500809