基础通信模型(基于UDP协议)

1,051 阅读4分钟

这是我参与11月更文挑战的第十三天,活动详情查看:2021最后一次更文挑战

什么是UPD传输协议

UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。

目的和要求

1.    了解网络协议的基本概念;

2.    掌握UDP基础通信模型的服务器与客户端的通讯过程;

3.    掌握SOCKET的创建;

4.    了解gets和scanf的区别。

内容和步骤

服务器端:

1、包含网络头文件网络库

#include <stdio.h>
//1、包含网络头文件网络库
# include <WinSock2.h>
# pragma comment(lib, "Ws2_32.lib")
#pragma warning(disable:4996)

2、打开网络库

//2、打开网络库
	int iret = WSAStartup(wVersionRequested, &wsaDATA);
	if (iret != 0)
	{
		//有错
		switch (iret)
		{
		case WSASYSNOTREADY:
			printf("解决方案:重启。。。");
			break;
		case WSAVERNOTSUPPORTED:
			printf("解决方案:更新网络库");
			break;
		case WSAEINPROGRESS:
			printf("解决方案:重启。。。");
			break;
		case WSAEPROCLIM:
			printf("解决方案:网络连接达到上限或阻塞,关闭不必要软件");
			break;
		case WSAEFAULT:
			printf("解决方案:程序有误");
			break;
		}
		getchar();
		return 0;
	}

3、校验版本

if (2 != HIBYTE(wsaDATA.wVersion) || 2 != LOBYTE(wsaDATA.wVersion))
	{
		printf("版本有问题!");
		WSACleanup();//关闭网络库
		return 0;
	}

4、创建SOCKET

socketServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == socketServer)
	{
		//清理网络库,不关闭句柄
		WSACleanup();
		return 0;
	}

5、绑定地址与端口

struct sockaddr_in si;
	si.sin_family = AF_INET;//这里要和创建SOCKET句柄的参数1类型一样
	si.sin_port = htons(9527);//用htons宏将整型转为端口号的无符号整型
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	if (bind(socketServer, (struct sockaddr*)&si, sizeof(si)) == SOCKET_ERROR)
	{
		int err = WSAGetLastError();//取错误码
		printf("服务器bind失败错误码为:%d\n", err);
		closesocket(socketServer);//释放
		WSACleanup();//清理网络库
	}

7.与客户端收发消息

while (1)
	{
		//收
		struct sockaddr sa;
		int iSaLen = sizeof(sa);
		char strRecvBuff[548] = { 0 };
		char csendbuff[1500] = { 0 };

		if (recvfrom(socketServer, strRecvBuff, 548, 0, &sa, &iSaLen) == SOCKET_ERROR)
		{
			int err = WSAGetLastError();//取错误码
			printf("服务器recvfrom失败错误码为:%d\n", err);
			continue;
		}
		printf("服务器recvfrom消息是:%s\n", strRecvBuff);

		//发
		//if (sendto(socketServer, "This is a message from server~!", sizeof("This is a message from server~!"), 0, &sa, sizeof(sa)) == SOCKET_ERROR)
		//{
		//	int err = WSAGetLastError();//取错误码
		//	printf("服务器sendto失败错误码为:%d\n", err);
		//	continue;
		//}
		scanf("%s", csendbuff);
		if (send(socketServer, csendbuff, sizeof(csendbuff), 0) == SOCKET_ERROR)
		{
			int err = WSAGetLastError();//取错误码
			printf("server send失败错误码为:%d\n", err);
			//不用关闭socket
			//根据实际情况处理
		}

	}

客户端:

1、包含网络头文件网络库

#include <stdio.h>
//1、包含网络头文件网络库
# include <WinSock2.h>
# pragma comment(lib, "Ws2_32.lib")
#pragma warning(disable:4996)

2、打开网络库

//2、打开网络库
	int iret = WSAStartup(wVersionRequested, &wsaDATA);
	if (iret != 0)
	{
		//有错
		switch (iret)
		{
		case WSASYSNOTREADY:
			printf("解决方案:重启。。。");
			break;
		case WSAVERNOTSUPPORTED:
			printf("解决方案:更新网络库");
			break;
		case WSAEINPROGRESS:
			printf("解决方案:重启。。。");
			break;
		case WSAEPROCLIM:
			printf("解决方案:网络连接达到上限或阻塞,关闭不必要软件");
			break;
		case WSAEFAULT:
			printf("解决方案:程序有误");
			break;
		}
		getchar();
		return 0;
	}

3、校验版本

if (2 != HIBYTE(wsaDATA.wVersion) || 2 != LOBYTE(wsaDATA.wVersion))
	{
		printf("版本有问题!");
		WSACleanup();//关闭网络库
		return 0;
	}

4、创建SOCKET

socketClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == socketClient)
	{
		//清理网络库,不关闭句柄
		WSACleanup();
		return 0;
	}

	//服务器地址与端口
	struct sockaddr_in si;
	si.sin_family = AF_INET;//这里要和创建SOCKET句柄的参数1类型一样
	si.sin_port = htons(9527);//用htons宏将整型转为端口号的无符号整型
	si.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	//客户端不用绑定操作
	/*
	if(bind(socketServer,(struct sockaddr*)&si,sizeof(si))==SOCKET_ERROR)
	{
		int err = WSAGetLastError();//取错误码
		printf("服务器bind失败错误码为:%d\n",err);
		closesocket(socketServer);//释放
		WSACleanup();//清理网络库
	}
	*/

5、与服务器端收发消息

while (1)
	{
		//发
		char strSendBuff[548] = { 0 };
		scanf("%s", strSendBuff);
		if (sendto(socketClient, strSendBuff, sizeof(strSendBuff), 0, (const struct sockaddr*)&si, sizeof(si)) == SOCKET_ERROR)
		{
			int err = WSAGetLastError();//取错误码
			printf("客户端sendto失败错误码为:%d\n", err);
			continue;
		}

		//收
		struct sockaddr sa;
		int iSaLen = sizeof(sa);
		char strRecvBuff[548] = { 0 };

		if (recvfrom(socketClient, strRecvBuff, 548, 0, &sa, &iSaLen) == SOCKET_ERROR)
		{
			int err = WSAGetLastError();//取错误码
			printf("客户端recvfrom失败错误码为:%d\n", err);
			continue;
		}
		printf("客户端recvfrom消息是:%s\n", strRecvBuff);

	}

记住,在使用完网络库之后一定要关闭。

closesocket(socketClient);//与4、创建SOCKET对应,如果有创建客户端SOCKET句柄,也要关闭
	//关闭网络库
	WSACleanup();
	system("pause");
	return 0;

UDP基础通信模型与TCP基础通信模型有什么区别?

1、UDP简单,速度更快,更高效,(无连接,无验证,头信息少协议代码判断就少);

2、数据不可靠,(无验证);

3、不管是数据报,还是字节流,都是在传输线路上传输的数据,对于传输线路而言是没有区别的,区别在于数据到了传输层之后的处理方式,数据报就是数据报的形式处理,字节流就以流的特点处理。

小结

如果这篇文章对你有帮助的话,记得三连凹~