[windows]原生c++ Tcp socket通信简单详解

又是幻想进鹅厂的一天 ┭┮﹏┭┮

这个是当时Qt的 tcpServer :https://blog.csdn.net/weixin_42837024/article/details/88847220

写Qt写的想了一下,原生的 c++ tcp是怎么写的?我还不会,网上查了查资料 自己写了个小demo玩一玩
发现tcp socket 通信都是那回事

下面先介绍一下 TCP 的基础知识( 还挺重要的 毕竟大厂挺注重基础的)

TCP :Tcp 协议传输控制协议(Transmission Control Protocol)
	      1.面向连接的 :先连接,在通信,比如电话?
	      					
	      2.可靠的:基于面向连接机制,3次握手协议 等,要比UDP 可靠

	      3.基于字节流的:这就意味着数据是以字节流的形式传递给接收者的,
	      没有固有的”报文”或”报文边界”的概念。从这方面来说,读取TCP数据
	      就像从串行端口读取数据一样–无法预先得知在一次指定的读调用中会
	      返回多少字节(也就是说能知道总共要读多少,但是不知道具体某一次读多少)。
	      UDP创建UDP socket——DGRAM:基于数据报通信方式,每一次发送的数据
	      都是一个独立的整体,包含目标主机的ip地址、端口号及发送数据的内容
3次握手和4次握手

在这里插入图片描述

3次握手 通俗讲:

假设一个为A 一个为B

(第一次): A问B 你听得到吗?(说明A能发出声音 代表可以send)
(第二次): B说我听到了,你能听到吗?(说明B能听到声音 代表可以read)
(第三次): A说我也听到了,那咱们开始吧(说明A也能听到read,B也能发声音send)

如果是2次握手的话 不能确保可靠性,4次的话就冗余了,所以就是3次握手

4次挥手

(第一次):A说 我说完了,你还有话说吗(因为不确定B还有没有话说)
(第二次):B说 等等我还有一句话 (i love you)
(第三次):A听完了 说现在还有想说的吗 B说 没有了,想说的都说了,挂了吧
(第四次):A说 好 挂了吧 A挂 B挂

tcp 编程一般的步骤:

server(服务端)

1.new 个 socket
2.bind (IP,port)
3.listen,等待客户端的连接
4.accept 接受客户端的连接
5.接收(recv) 发送数据(send)
6.关闭连接

client(客户端)

1.new 个 socket
2.bind(ip,port)
3.connect (server.ip,server,port)
4.recv() /send()
5.关闭连接

基本知识介绍完了,说一下我们用的库
#include <WinSock2.h>

WSAData 这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets数据。它包含Winsock.dll执行的数据。

在这里插入图片描述

说一下这个结构体 各条的含义

wVersion

Windows Sockets DLL期望调用者使用的Windows Sockets规范的版本。
高位字节存储副版本号, 低位字节存储主版本号,可以用WORD MAKEWORD(BYTE,BYTE )
返回这个值,例如:MAKEWORD(2,2)

wHighVersion
这个dll 支持的windows socket 的最高版本

szDescription
以null结尾的ASCII字符串,Windows Sockets DLL将对Windows Sockets实现的描述
拷贝到这个字符串中,包括制造商标识。文本(最多可以有256个字符)可以包含任何
字符,但是要注意不能包含控制字符和格式字符,应用程序对其最可能的使用方式是
把它(可能被截断)显示在在状态信息中。

szSystemStatus
以null结尾的ASCII字符串,Windows Sockets DLL把有关的状态或配置信息拷贝
到该字符串中。Windows Sockets DLL应当仅在这些信息对用户或支持人员有用
时才使用它们,它不应被作为szDescription域的扩展。

iMaxSockets
单个进程能够打开的socket的最大数目。Windows Sockets的实现能提供一个全局
的socket池,可以为任何进程分配;或者它也可以为socket分配属于进程的资源。
这个数字能够很好地反映Windows Sockets DLL或网络软件的配置方式。
应用程序的编写者可以通过这个数字来粗略地指明Windows Sockets的实现方式
对应用程序是否有用。例如,X Windows服务器在第一次启动的时候可能会检查
iMaxSockets的值:如果这个值小于8,应用程序将显示一条错误信息,指示用户
重新配置网络软件(这是一种可能要使用szSystemStatus文本的场合)。显然
无法保证某个应用程序能够真正分配iMaxSockets个socket,因为可能有其它
WindowsSockets应用程序正在使用。

iMaxUdpDg
Windows Sockets应用程序能够发送或接收的最大的用户数据包协议(UDP)的数据包大小
以字节为单位。如果实现方式没有限制,那么iMaxUdpDg为零 。在Berkeley sockets的
许多实现中,对于UDP数据包有个固有的限制(在必要时被分解) ,大小为8192字节。
Windows Sockets的实现可以对碎片重组缓冲区的分配作出限制。
对于适合的WindowsSockets 实现,iMaxUdpDg的最小值为512。注意不管iMaxUdpDg的值是什么,
都不推荐你发回一个比网络的最大传送单元(MTU)还大的广播数据包。
(Windows Sockets API 没有提供发现MTU的机制,但是它不会小于512个字节)。
WinSock2.0版中已被废弃。

lpVendorInfo
指向销售商的数据结构的指针。这个结构的定义(如果有)超出了WindowsSockets规范的范围
WinSock2.0版中已被废弃。

code vs2017 win32 (server:)

// tcpServer.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WinSock2.h>
/*
*
表示链接ws2_32.lib这个库。和在工程设置里写上链入ws2_32.lib的效果一样(两种方式等价,或说一个隐式一个显式调用),
*/
#pragma comment(lib,"ws2_32.lib")

#define _WINSOCK_DEPRECATED_NO_WARNINGS 
/* 不加这个宏的话会有这个错误
* inet_addr': Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS
* to disable deprecated API warnings	
 */






int main()
{
	int result = 0;

	SOCKET server_ = INVALID_SOCKET; //socket 对象

	WSADATA data_;	

	result = WSAStartup(MAKEWORD(2, 2), &data_);//inital
	if (result != 0)
	{
		std::cout << "WSAStartup() init error "  <<GetLastError()<<std::endl;
		system("pause");
		return 1;
	}

	server_ = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(11000);
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//ip port 

	result = bind(server_, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
	if (result != 0)
	{
		std::cout << "bind() error" << result;
		system("pause");
		return 1;
	}
	result = listen(server_, 10);
	if (result != 0)
	{
		std::cout << "listen error"<<result;
		system("pause");
		return 1;
	}

	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);


	std::cout << "wait new connect......" << std::endl;
	SOCKET socketConn = accept(server_, (SOCKADDR*)&addrClient, &len);

	if (socketConn == SOCKET_ERROR)
	{
		std::cout << " accept error" << WSAGetLastError();
		return 1;
	}


	while (1)
	{


		char *f;

		f = inet_ntoa(addrClient.sin_addr);
		std::cout << "accept client ip:" << f << std::endl;

		char buff[] ="hello i am server\n";

		int iSend = send(socketConn, buff, sizeof(buff), 0);
		if (iSend == SOCKET_ERROR)
		{
			std::cout << "send error";
			break;
		}

		char recvBuff[1024];
		memset(recvBuff, 0, sizeof(recvBuff));

		recv(socketConn, recvBuff, sizeof(recvBuff), 0);

		std::cout << "recv from client:" << recvBuff << std::endl;;

		//closesocket(socketConn);


	}

	closesocket(server_);
	WSACleanup();

	system("pause");

	return 0;

	
}


code (client:)

// tcpClient.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")


int main()
{
   
	WSADATA data;

	if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
	{
		std::cout << "WSAStartup error";
		system("pause");
		return 1;
	}

	SOCKADDR_IN addrServer;
	addrServer.sin_family = AF_INET;
	addrServer.sin_port = htons(11000);
	addrServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	SOCKET socketClient = socket(AF_INET, SOCK_STREAM, 0);
	if (socketClient == INVALID_SOCKET)
	{
		std::cout << "socket create error";
		system("pause");
		return 1;
	}

	if (connect(socketClient, (struct sockaddr*)&addrServer, sizeof(addrServer)) == INVALID_SOCKET)
	{
		std::cout << "connect error";
		system("pause");
		return 1;
	}

	std::cout << "connect success,wait data......" << std::endl;;

	while (1)
	{
		char sendData[] = "hello i am client\n";
		int a;


		send(socketClient, sendData, sizeof(sendData), 0);

		char recvBuff[1024];
		memset(recvBuff, 0, sizeof(recvBuff));

		if (recv(socketClient, recvBuff, sizeof(recvBuff), 0) <= 0)
		{
			std::cout << "recv error";
			break;
		}

		std::cout << "recv from server:" << recvBuff << std::endl;


	}

	closesocket(socketClient);

	WSACleanup();


	return 0;


}


在这里插入图片描述

又是幻想进鹅厂的一天 ┭┮﹏┭┮

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值