windows Socket编程之UDP的服务端和客户端

上一篇讲了TCP的服务端和客户端,本篇文章来介绍一下UDP的服务端和客户端。

相比TCP来说,UDP相对比较简单,刚开始的时候,和TCP一样都需要先进行网络环境的初始化,即调用WSAStartup函数。然后呢,我们也需要创建一个socket,这个socket和TCP的那个socket不同,上篇提过TCP创建一个socket调用socket函数时,第二个参数为SOCK_STREAM,而UDP则需要给定一个SOCK_DGRAM,然后在第三个参数上给一个IPPROTO_UDP,这样我们就创建好了一个UDP的socket。

接下来,也和TCP一样,指定SOCKADDR_IN的地址信息(端口,ip),指定完之后呢,若是客户端,则可以直接就进行通信了,若是服务端,则还需要增加一步bind操作,当我们调用bind函数,进行绑定后,服务端就可以和客户端进行通信了。而TCP的服务端还有两个步骤,一个是listen,一个是accept,UDP省略了这两个步骤。

上篇提到TCP进行数据的收发是通过recv和send两个API来进行数据的收发的。而UDP也需要两个函数,叫做recvform和sendto,这两个和TCP那两个有点不同,其声明如下:

int recvfrom(  
  SOCKET s,                     //socket
  char FAR* buf, 		//接收数据的缓冲区             
  int len,                      //缓冲区的大小
  int flags,                    //标志位,调用操作方式
  struct sockaddr FAR *from,    //sockaddr结构地址
  int FAR *fromlen              //sockaddr结构大小地址
);
int sendto(  
  SOCKET s,                    		//socket      
  const char FAR *buf,         		//发送数据的缓冲区   
  int len,                    		//缓冲区大小      
  int flags,                       	//标志位,调用操作方式
  const struct sockaddr FAR *to,    	//sockaddr结构地址
  int tolen                        	//sockaddr结构大小地址
);
注意,这两个函数里边有一个sockaddr结构地址,它是用来保存该数据发送者的信息的。上篇提过,TCP是面向连接的,它在通信之前需要进行三次握手来确定双方是否已经准备好了。因此,双方很清楚数据是从哪里来的。而UDP是面向数据包的,因此就好像寄快递一样,你必须在快递上写一张纸条,上面填好姓名,地址等信息,填好之后,接收者才知道该东西是由谁寄过来的。因此,上面两个函数提供了sockaddr结构的地址,用于保存从哪里发来的和发送到哪里的地址信息。

以下是UDP的服务端和客户端的示例代码:

服务端:

#include 
#include 
#include 

#pragma comment(lib,"ws2_32.lib")
#define  PORT 6000

int main(int argc, char* argv[])
{
	//初始化网络环境
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
	{
		printf("WSAStartup failed\n");
		return -1;
	}

	//建立一个UDP的socket
	SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sock == SOCKET_ERROR)
	{
		printf("create socket failed\n");
		return -1;
	}

	//绑定地址信息
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;
	serverAddr.sin_port = htons(PORT);
	serverAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);

	bind(sock, (sockaddr*)&serverAddr, sizeof(sockaddr));
	
	char buf[512];
	while (TRUE)
	{
		memset(buf, 0, 512);
		// 网络节点的信息,用来保存客户端的网络信息
		sockaddr_in clientAddr;
		memset(&clientAddr, 0, sizeof(sockaddr_in));

		int clientAddrLen = sizeof(sockaddr);
		//接收客户端发来的数据
		int ret = recvfrom(sock, buf, 512, 0,(sockaddr*) &clientAddr,&clientAddrLen );
		
		printf("Recv msg:%s from IP:[%s] Port:[%d]\n", buf,inet_ntoa(clientAddr.sin_addr),ntohs(clientAddr.sin_port));
		// 发一个数据包返回给客户
		sendto(sock, "Hello World!", strlen("Hello World!"), 0, (sockaddr*)&clientAddr, clientAddrLen);
		printf("Send msg back to IP:[%s] Port:[%d]\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port));
	}
	return 0;
}

客户端:

#include 
#include 
#include 

#pragma comment(lib,"ws2_32.lib")
#define  PORT 6000
int main(int argc, char* argv[])
{
	//初始化网络环境
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
	{
		printf("WSAStartup failed\n");
		return -1;
	}
	//建立一个UDP的socket
	SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sockClient == INVALID_SOCKET)
	{
		printf("create socket failed\n");
		return -1;
	}
	// 申明一个网络地址信息的结构体,保存服务器的地址信息
	sockaddr_in addr = { 0 };
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	char buf[] = "client test!";
	//发送数据
	int dwSent = sendto(sockClient, buf, strlen(buf), 0, (SOCKADDR *)&addr, sizeof(SOCKADDR));
	if (dwSent == 0)
	{
		printf("send %s failed\n", buf);
		return -1;
	}
	printf("send msg:%s\n", buf);

	char recvBuf[512];
	memset(recvBuf, 0, 512);

	sockaddr_in addrSever = { 0 };
	int nServerAddrLen=sizeof(sockaddr_in);
	// 接收数据
	int dwRecv = recvfrom(sockClient, recvBuf, 512, 0, (SOCKADDR *)&addrSever,&nServerAddrLen);
	printf("Recv msg from server : %s\n", recvBuf);
	//关闭SOCKET连接
	closesocket(sockClient);
	//清理网络环境
	WSACleanup();
	system("pause");
	return 0;
}

你可能感兴趣的:(windows编程)