C++ TCP 网络编程与数据通信

 

服务器端: 

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 
#include <Windows.h>
#include <string>


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)<<":"<<remoteAddr.sin_port << std::endl;


		//创建一个线程与客户端进行会话
		CreateThread(NULL, 0, ThreadTCP, (LPVOID)sClient, 0, NULL);

	}

	closesocket(slisten);

	//winsock的注销函数,从底层的Windows Sockets DLL 中撤销注册
	WSACleanup();
	return 0;
}

DWORD WINAPI ThreadTCP(LPVOID pParam)
{
	SOCKET sClient = (SOCKET)pParam;

	char revData[255];
	std::string data = "hello_";
	int n = 0;
	while (1)
	{
		data = "hello_"+std::to_string(n);
		//接收数据  
		//从一个已经与对方建立连接的socket接收数据	
		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(5000);
	}


	//断开连接
	std::cout << "断开连接" << std::endl;
	//关闭socket,释放相应的资源
	closesocket(sClient);

	return 0;
}

 客户端程序:

#include <winsock.h> 
#include<iostream>
#pragma comment(lib,"ws2_32.lib") 

#include <Windows.h>
#include<string>

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_";

	int n = 0;
	while (1)
	{
		data = "abcdef_"+std::to_string(n);
		//std::cin >> data;
		const char * sendData;
		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, strlen(sendData), 0);

		//接收返回的数据
		char recData[255];
		int ret = recv(sclient, recData, 255, 0);
		if (ret > 0)
		{
			recData[ret] = 0x00;
			std::cout <<"第"<<n<<"回:"<< recData << std::endl;
		}

		if (n++>10)
		{
			break;
		}

		Sleep(500);
	}

	//关闭
	closesocket(sclient);

	WSACleanup();
	system("pause");
	return 0;
}

 

批注:

数据包1:客户端从端口4671向服务器端口8888发起连接

数据包2:8888应答连接

数据包3:4671应答8888的应答,成功建立连接

 

客户端程序先send("abcdef_0")【数据包4】,然后阻塞在recv()处等待服务器返回数据,

服务器端程序阻塞在recv,然后接收到客户端程序发送来的"abcdef_0",显示在控制台,

服务器端发送send("hello_0")【数据包6】,然后Sleep(5000)

 

客户端接收到recv("hello_0"),显示在控制台,然后Sleep(500)

第1个500ms后, 客户端继续发送send("abcdef_1")【数据包8】,然后阻塞在recv()处

服务器端还在Sleep(5000)

 

直到服务器端Sleep完毕,然后recv("abdef_1"),显示在控制台,并且send("hello_1")【数据包10】,然后Sleep(5000)

客户端从recv("hello_1")处执行,显示在控制台,然后Sleep(500)

 

客户端Sleep(500)完毕后,又向服务器端发送send("abcdef_2")【数据包12】,然后阻塞在recv()处

服务器端还在Sleep(5000)

 

直到服务器端Sleep完毕,然后recv("abcdef_2").......重复进行

 

从分析流程看出,服务器端控制台和客户端控制台显示分别显示"abcdef_xx"   "hello_xx"是基本上同时的

二者的流程控制主要被服务器端的Sleep(5000)拖延,

客户端则相应的在recv()处等待数据

 

客户端4671发送的最后一个数据"abcdef_11"【数据包48】,之后阻塞5s等待服务器端Sleep完毕返回数据"hello_11"【数据包50】

之后客户端触发了     

   if (n++>10)
        {
            break;
        }

执行,closesocket(sclient)了,此时,客户端发送了结束会话请求【数据包52】

数据包53:8888应答了4671的结束请求

此后4671还没有继续响应结束请求

在第60s时,8888又向4671发送了数据"hello_12" 【数据包54】,显然该数据客户端不会再recv()了,客户端程序已经完全退出

最后一个数据包55,是4671发送的响应结束会话,之后两个端口间不再通信

 

问题:

服务器端在第60从Sleep(5000)醒来后,为什么没有阻塞在recv()处,此时并没有收到任何数据,而是向下执行,并且发送了第54个数据包"hello_12"?

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值