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多对多时需要进行地址管理,接下来的目标