客户端程序:
#include
#include
#pragma comment(lib,"ws2_32.lib")
#include
#include
int main()
{
//初始化Windows Socket Application
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
//WinSock的注册函数,初始化底层的Windows Sockets DLL
if (WSAStartup(sockVersion, &wsaData) != 0)
return 0;
//创建一个socket并返回socket的标识符
SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sclient == INVALID_SOCKET)
{
std::cout << "invalid socket!" << std::endl;
return 0;
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{ //连接失败
std::cout << "connect error !" << std::endl;
closesocket(sclient);
return 0;
}
std::string data="abcdef_";
char* chs = new char[1000];
for (int i = 0; i < 1000; ++i)
chs[i] = i;
const char * sendData = chs;
char recData[255];
int n = 0;
while (1)
{
//data = "abcdef_"+std::to_string(n);
//std::cin >> data;
//sendData = data.c_str();
//string转const char*
/* send()用来将数据由指定的socket传给对方主机
int send(int s, const void * msg, int len, unsigned int flags)
s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error */
send(sclient, sendData, 1000, 0);//一次发送多,接收少
//接收返回的数据
int ret = recv(sclient, recData, 255, 0);//阻塞在此处等待接收
if (ret > 0)
{
recData[ret] = 0x00;
std::cout <<"第"<10)
{
break;
}
Sleep(1000);
}
delete[] chs;
//关闭
closesocket(sclient);
WSACleanup();
system("pause");
return 0;
}
服务器端程序:
#include
#include
#pragma comment(lib,"ws2_32.lib")
#include
#include
DWORD WINAPI ThreadTCP(LPVOID pParam);
int main()
{
//初始化Windows Socket Application
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
//WinSock的注册函数,初始化底层的Windows Sockets DLL
if (WSAStartup(sockVersion, &wsaData) != 0)
return 0;
//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
std::cout << "create socket error!" << std::endl;
return 0;
}
//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
//
sin.sin_port = htons(8888);//指定端口,将端口号转换为网络字节顺序
sin.sin_addr.S_un.S_addr = INADDR_ANY;
//bind()把socket绑定到特定的网络地址上
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
std::cout << "bind error!" << std::endl;
return 0;
}
//开始监听
//启动指定的socket,监听到来的连接请求
5指定了监听socket的等待连接缓冲区队列的最大长度,一般设为5
if (listen(slisten, 5) == SOCKET_ERROR)
{
std::cout << "listen error !" << std::endl;
return 0;
}
//循环接收数据
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
while (true)
{
std::cout << "阻塞。。。。等待连接。。。" << std::endl;
//接收一个连接请求,并新建一个socket,原来的socket返回监听状态
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
std::cout << "accept error !" << std::endl;
continue;
}
//inet_addr()把一个标准的点分十进制的IP地址转换成长整型的地址数据
//inet_ntoa()把长整型的IP地址数据转换成点分十进制的ASCII字符串
std::cout << "接受一个连接:" << inet_ntoa(remoteAddr.sin_addr)<<":"<导致接收端满
int ret = recv(sClient, revData, 255, 0);//阻塞于此进行接收
if (ret > 0)
{
revData[ret] = 0x00;
std::cout << "第" << n++ << "回:" << revData << std::endl;
//printf(revData);
}
//发送数据
const char * sendData = data.c_str();
向一个已经与对方建立连接的socket发送数据
int nb=send(sClient, sendData, strlen(sendData), 0);
if (nb != strlen(sendData))
{
break;
}
//Sleep(1000);
}
//断开连接
std::cout << "断开连接" << std::endl;
//关闭socket,释放相应的资源
closesocket(sClient);
return 0;
}
下面进行详细分析:
前3个数据包建立TCP通信
然后客户端5875发送1000个字节【数据包4】,服务器端8888返回确认【数据包5】,客户端程序阻塞在recv()
服务器端程序阻塞在recv(),然后接收recv()255个字节,显示在控制台"第0回"(为什么没有正常显示??),服务器端发送send("hello_0")【数据包6】,下面的a),b)同时在进行
a)服务器程序进入下一个循环,由于接收缓存还有数据(1000-255),继续recv()255个字节,这个循环没有被阻塞,然后继续send("hello_1")【数据包8】,再进入下一个循环,由于接收缓存还有数据(1000-255*2),继续recv()255个字节,send("hello_2")【数据包10】
再进入下一个循环,从剩余的(1000-255*3=235)个字节中取出235个字节,send("hello_3")【数据包12】,
进入下个循环,暂时没有数据了,阻塞在recv()
b)客户端程序recv("hello_0"),显示在控制台,然后Sleep(1000)
接下来,客户端程序从Sleep(1000)中醒来,又发送1000个字节【数据包14】,由于客户端接收缓存已经接收到"hello_1" "hello_2" "hello_3",所以接下来一次性recv()完,显示在了控制台,然后Sleep(1000)
服务器端又开始每次接收255字节,把刚才客户端发来的1000个字节分次接收,每次接收时再send("hello_4")【数据包16】,send("hello_5")【数据包18】,send("hello_6")【数据包20】,send("hello_7")【数据包22】
一直进行到第114个数据包,客户端发送1000个字节,接收完第11回,显示在控制台 第11回:hello_40hello_41hello_42hello_43
客户端通过break退出了循环
关闭了通信【数据包116】
服务器发送的117数据包,客户端当然不再接收
服务器本来应该把最后的1000个字节分次recv()出来,但是由于它send()失败,导致了break退出循环
所以服务器也没有把最后的1000个字节完全取出