网络编程——简单UDP套接字编程

UDP许多函数与TCP是一样的,就不重复介绍,介绍不一样的。
TCP:https://blog.csdn.net/weixin_42109012/article/details/105308403

一、通讯流程

UDP通讯与TCP通讯相比,少了很多步骤(双方不用同时连接)。
1、服务端绑定数据接收端口
2、客户端向服务端IP、端口发信息
3、服务端接收某端口的数据
4、服务器端往对应客户端IP和端口发信息
5、客户端接收数据
6、断开连接

在这里插入图片描述

二、一些介绍

多对多,对通讯质量要求不高,多用于实时聊天室

1、服务器端
无连接,1个套接字,没有客户端数的限制。
基于recvfrom获得的地址结构反馈数据(sendto)。
地址需要进行管理。

2、客户端
不需要连接,可以同时给多个服务器端发送数据。

三、函数介绍(建议看官方文档)

UDP通讯与TCP通讯相比,简单UDP编程有两个特殊的函数。
send()---->sendto()
recv()---->recvfrom()

1、socket()——套接字
创建套接字需要使用数据包、UDP协议。

socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

2、sendto()——发送数据到指定地址
sendto (SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen);
(1)s:发送套接字。
(2)buf:包含待发送数据的缓冲区。
(3)len:缓冲区中数据的长度。
(4)flags:调用执行方式(建议都写0)
(5)to:指定地址
(6)tolen:指定地址长度

示例:

sockaddr_in server_addr;
int server_addr_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5678);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
sendto(client_socket, SEND_BUFF, strlen(SEND_BUFF), 0, (sockaddr*)&server_addr, server_addr_len);

提示:
值得一提的是 sendto 有时候相当于 connect + send,可以先用 connect 连接固定地址,然后发送数据(但 UDP 不推荐使用)。

sockaddr_in server_addr;
int server_addr_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5678);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
connect(client_socket, (sockaddr*)&server_addr, sizeof(server_addr));	//连接地址,固定传输
send(client_socket, SEND_BUFF, strlen(SEND_BUFF), 0);

3、recvfrom()——从指定地址接收数据
recvfrom(SOCKET s, char *buf, int len, int flags, sockaddr *from, int *fromlen);
(1)s:发送套接字。
(2)buf:包含待发送数据的缓冲区。
(3)len:缓冲区中数据的长度。
(4)flags:调用执行方式(建议都写0)
(5)from:指定地址
(6)fromlen:指定地址长度
(7)返回值:成功:>0,接收数据长度;出错:0,未接收到数据,负数,关闭连接。

示例:

sockaddr_in server_addr;
int server_addr_len = sizeof(server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5678);
server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
recvfrom(client_socket, RECV_BUFF, BUFF_SIZE, 0, (sockaddr*)&server_addr, &server_addr_len);

四、网络编程——简单UDP套接字编程

仅实现单个客户端连接发送数据,且服务端和客户端使用 winsock.h

1、服务端——server.cpp

/*****************************************************************************
* @author  : ljf                                                             *
* @date    : 2020/3/24                                                       *
* @file    : Server.cpp                                                      *
* @brief   : 基于UDP协议通信——服务端                                       *
*----------------------------------------------------------------------------*
*                           Change History                                   *
*----------------------------------------------------------------------------*
* Date        | Version   | Author         | Description                     *
*----------------------------------------------------------------------------*
* 2020/3/24   | 1         | ljf            | 创建并简单实现                  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <winsock.h>

#pragma comment(lib, "wsock32.lib")	//隐式加载

int main() {
	//服务器监听端口
	int SERVER_PORT = 5678;
	//缓冲区大小
	const int BUFF_SIZE = 1024;
	char SEND_BUFF[] = "已收到";
	char RECV_BUFF[BUFF_SIZE];

	//初始化套接字
	WSADATA wsadata;
	if (0 != WSAStartup(MAKEWORD(1, 1), &wsadata)) {
		printf("WSAStartup error ---- Error Code: %d", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	//创建客户端套接字(监听套接字)
	SOCKET server_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == server_socket) {
		printf("Create socket error ---- Error Code: %d", WSAGetLastError());
		WSACleanup();
		return -2;
	}

	//初始化监听信息
	sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	if (SOCKET_ERROR == bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr))) {
		printf("socket bind error ---- Error Code: %d", WSAGetLastError());
		WSACleanup();
		return -3;
	}

	sockaddr_in client_addr;
	int client_addr_len = sizeof(client_addr);
	printf("Listen port %d...\n", SERVER_PORT);
	
	//信息交互
	while (true) {
		//清空缓冲区
		memset(RECV_BUFF, 0, BUFF_SIZE);
		//有数据返回
		if (0 < recvfrom(server_socket, RECV_BUFF, BUFF_SIZE, 0, (sockaddr*)&client_addr, &client_addr_len)) {
			printf("[Recv %s:%d]: %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), RECV_BUFF);
			sendto(server_socket, SEND_BUFF, BUFF_SIZE, 0, (sockaddr*)&client_addr, client_addr_len);
		}
		//客户端排除0,负数为关闭了客户端
		else {
			break;
		}
	}
	closesocket(server_socket);
	WSACleanup();
	system("pause");
	return 0;
}

2、客户端——client.cpp

/*****************************************************************************
* @author  : ljf                                                             *
* @date    : 2020/3/24                                                       *
* @file    : Client.cpp                                                      *
* @brief   : 基于UDP协议通信——客户端                                       *
*----------------------------------------------------------------------------*
*                           Change History                                   *
*----------------------------------------------------------------------------*
* Date        | Version   | Author         | Description                     *
*----------------------------------------------------------------------------*
* 2020/3/24   | 1         | ljf            | 创建并简单实现                  *
*****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <winsock.h>

#pragma comment(lib, "wsock32.lib")	//隐式加载

int main() {
	//服务器端口
	int SERVER_PORT = 5678;
	//服务器IP
	char SERVER_IP[20] = "127.0.0.1";
	//缓冲区大小
	const int BUFF_SIZE = 1024;
	char SEND_BUFF[BUFF_SIZE];
	char RECV_BUFF[BUFF_SIZE];

	//初始化套接字
	WSADATA wsadata;
	if (0 != WSAStartup(MAKEWORD(1, 1), &wsadata)) {
		printf("WSAStartup error ---- Error Code: %d", WSAGetLastError());
		WSACleanup();
		return -1;
	}

	//创建客户端套接字
	SOCKET client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (INVALID_SOCKET == client_socket) {
		printf("Create socket error ---- Error Code: %d", WSAGetLastError());
		WSACleanup();
		return -2;
	}

	//初始化服务端连接信息
	sockaddr_in server_addr;
	int server_addr_len = sizeof(server_addr);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(5678);
	server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);

	//信息交互
	while (true) {
		//清空缓冲区
		memset(SEND_BUFF, 0, BUFF_SIZE);
		memset(RECV_BUFF, 0, BUFF_SIZE);

		printf("[Send]: ");
		//读取一行字符串,回车结束,回车会被读入
		fgets(SEND_BUFF, BUFF_SIZE, stdin);
		//处理末尾的回车
		SEND_BUFF[strlen(SEND_BUFF) - 1] = '\0';

		//收发数据
		if (strlen(SEND_BUFF) != 0) {
			sendto(client_socket, SEND_BUFF, strlen(SEND_BUFF), 0, (sockaddr*)&server_addr, server_addr_len);
			recvfrom(client_socket, RECV_BUFF, BUFF_SIZE, 0, (sockaddr*)&server_addr, &server_addr_len);
			printf("[Recv]: %s\n", RECV_BUFF);
		}
	}
	closesocket(client_socket);
	WSACleanup();
	system("pause");
	return 0;
}

五、结果

在这里插入图片描述

六、总结

1、TCP连接和UDP连接
UDP中:connect作用,指定目的地址端口
TCP中:Connect建立连接,有三次握手信号

2、什么时候用UDP
(1)网络数据大多为短消息
(2)拥有大量Client
(3)对数据安全性无特殊要求
(4)网络负担非常重,但对响应速度要求高

3、地址管理
UDP多对多时需要进行地址管理,接下来的目标

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值