版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011857683/article/details/82564185
1 概述
(1) 基于UDP的服务器端流程
(a) 创建套接字(socket)
(b) 将套接字和IP地址、端口号绑定在一起(bind)
(c) 等待客户端发起数据通信(recvfrom/recvto)
(d) 关闭套接字
(2) 基于UDP的客户端流程
(a) 创建套接字(socket)
(b) 向服务器发起通信(recvfrom/recvto)
(c) 关闭套接字
(3) 基于UDP的socket编程流程图
基于UDP的socket编程不需要设置监听和发起/接收请求,可以直接相互通信,流程如下:
2 实例
2.1 UDP unix socket
(1) 客户端
############ udp_unix_socket_client.cpp ##############
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <pthread.h>
#include <malloc.h>
//unix socket文件路径
#define SERVER_UNIX_SOCKET_FILE "/tmp/server_unix_socket"
//unix socket文件路径长度
#define UNIX_SOCK_FILE_LEN (64)
//客户端socket发送缓冲区设置
#define SOCKET_BUFF (1024)
//socket接收发送超时
#define SOCKET_TIMEOUT_SEC (5)
/*
* 功能: 通过随机数生成一个文件路径
* 参数1: 生成文件存到数组
* 参数2: 数组大小
* 返回值, >=0表示成功, <0表示失败
*/
int getClientSocketFile(char *file_path, const int size)
{
if(NULL == file_path || size <= 0)
{
printf("getClientSocket_file file path is null or size(%d) is error!\n", size);
return -1;
}
memset(file_path, 0, size);
snprintf(file_path, size - 1, "/tmp/client_unix_socket_%u", (unsigned int)pthread_self());
return 0;
}
/*
* 功能: 客户端发送数据给服务端接口
* 参数1:服务端监听的文件路径
* 参数2:要发送的数据
* 参数3:要发送的数据长度
* 参数4: 接收的数据存放到数组
* 参数5: 接收数组大小
* 返回值:>=0表示成功, <0表示失败
*/
int socketSendData(const char* server_file,
const char* send_data,
const int send_data_size,
char* recv_data,
const int recv_data_size)
{
//参数空值检测
if(NULL == server_file || NULL == send_data || NULL == recv_data)
{
printf("socketSendData server_file or send_data or recv_data is null!\n");
return -1;
}
//参数大小检测
if(0 >= send_data_size || SOCKET_BUFF <= send_data_size ||
0 >= recv_data_size)
{
printf("socketSendData send_data_size(%d) or recv_data_size(%d) error!\n", send_data_size, recv_data_size);
return -1;
}
//创建客户端socket
int client_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
if(0 > client_sock)
{
printf("socketSendData create socket err(%d)!\n", errno);
return -1;
}
//设置超时时间
struct timeval times;
times.tv_sec = SOCKET_TIMEOUT_SEC;
times.tv_usec = 0;
int times_size = sizeof(times);
//接收超时设置
if(setsockopt(client_sock, SOL_SOCKET, SO_RCVTIMEO, ×, times_size))
{
printf("socketSendData setsockopt rcv time err(%d)!\n", errno);
close(client_sock);
return -1;
}
//发送超时设置
if(setsockopt(client_sock, SOL_SOCKET, SO_SNDTIMEO, ×, times_size))
{
printf("socketSendData setsockopt send time err(%d)!\n", errno);
close(client_sock);
return -1;
}
//设置发送缓冲区
int buflen = SOCKET_BUFF;
if(setsockopt(client_sock, SOL_SOCKET, SO_SNDBUF, &buflen, sizeof(buflen)))
{
printf("socketSendData setsockopt send buflen err(%d)!\n", errno);
close(client_sock);
return -1;
}
//设置接收缓冲区
if(setsockopt(client_sock, SOL_SOCKET, SO_RCVBUF, &buflen, sizeof(buflen)))
{
printf("socketSendData setsockopt recv buflen err(%d)!\n", errno);
close(client_sock);
return -1;
}
//获取一个随机文件路径
char client_file[UNIX_SOCK_FILE_LEN] = {0};
getClientSocketFile(client_file, sizeof(client_file));
printf("socketSendData client socket file(%s)!\n", client_file);
//绑定客户端socket文件
struct sockaddr_un addr_cli;
memset(&addr_cli, 0, sizeof(addr_cli));
addr_cli.sun_family = AF_UNIX;
strncpy(addr_cli.sun_path, client_file, sizeof(addr_cli.sun_path) - 1);
unlink(client_file);
if(bind(client_sock, (struct sockaddr*)&addr_cli, sizeof(addr_cli)) < 0)
{
printf("socketSendData tcp bind err(%d)!\n", errno);
close(client_sock);
return -1;
}
//构造服务端地址
struct sockaddr_un addr_svr;
memset(&addr_svr, 0, sizeof(addr_svr));
addr_svr.sun_family = AF_UNIX;
strncpy(addr_svr.sun_path, server_file, sizeof(addr_svr.sun_path) - 1);
if(connect(client_sock, (struct sockaddr*)&addr_svr, sizeof(addr_svr)) < 0)
{
printf("socketSendData tcp connect err(%d)!\n", errno);
close(client_sock);
unlink(client_file);
return -1;
}
//发送数据包
int len = send(client_sock, send_data, send_data_size, 0);
if(len != send_data_size)
{
printf("socketSendData unix socket send err(%d)!\n", errno);
close(client_sock);
unlink(client_file);
return -1;
}
//接收返回数据
len = recv(client_sock, recv_data, recv_data_size - 1, 0);
if(len <= 0)
{
printf("socketSendData unix socket recv err(%d)!\n", errno);
close(client_sock);
unlink(client_file);
return -1;
}
printf("socketSendData recv data(%s)!\n", recv_data);
close(client_sock);
unlink(client_file);
return 0;
}
int main()
{
char send_data[SOCKET_BUFF] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
char recv_data[SOCKET_BUFF] = {0};
socketSendData(SERVER_UNIX_SOCKET_FILE,
send_data,
strlen(send_data),
recv_data,
SOCKET_BUFF);
printf("%s\n", recv_data);
return 0;
}
(2) 服务端
############ udp_unix_socket_client.cpp ##############
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#include <pthread.h>
#include <malloc.h>
#include <signal.h>
//unix socket文件路径
#define SERVER_UNIX_SOCKET_FILE "/tmp/server_unix_socket"
//unix socket文件路径长度
#define UNIX_SOCK_FILE_LEN (64)
//客户端socket发送缓冲区设置
#define SOCKET_BUFF (1024)
//最大客户端连接数
#define BACKLOG (5)
//设备信息
#define JSON_DATA_1 "{" \
"\"method\": \"HOST_GET_Ret_Eui64\"," \
"\"longAddress\":[" \
"{\"eui64\": \"AAA\", \"type\": 1, \"deviceEndpoint\":1}," \
"{\"eui64\": \"BBB\", \"type\": 2, \"deviceEndpoint\":2}]" \
"}"
/*
* 功能: socket服务器
*/
void udpServer()
{
int i = 0;
int j = 0;
int ret = 0;
int udpSocket = -1;
struct sockaddr_un sun;
struct sockaddr_un remoteAddr;
char u8RecvBuf[SOCKET_BUFF] = {0};
unsigned int u32RemoteAddrLen = sizeof(remoteAddr);
printf("udpServer start\n");
//创建套接字
udpSocket = socket(AF_UNIX, SOCK_DGRAM, 0);
if(udpSocket < 0)
{
printf("tcpServer socket error!\n");
exit(1);
}
//绑定
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
strncpy(sun.sun_path, SERVER_UNIX_SOCKET_FILE, sizeof(sun.sun_path)-1);
unlink(SERVER_UNIX_SOCKET_FILE);
if((bind(udpSocket, (struct sockaddr *)&sun, sizeof(sun))) < 0)
{
printf("udpServer bind error(%d)!\n", errno);
close(udpSocket);
exit(1);
}
//忽略SIGPIPE信号,防止客户端关闭导致服务端退出
//signal(SIGPIPE, SIG_IGN);
//循环接收数据
while (1)
{
ret = recvfrom(udpSocket, u8RecvBuf, sizeof(u8RecvBuf), 0, (struct sockaddr *)&sun, &u32RemoteAddrLen);
if(ret < 0)
{
if(errno == EAGAIN || errno == EINTR)
{
//超时或者信号中断属于正常情况
printf("udpServer recv timeout or intrrupt signal continue recv!\n");
continue;
}
//socket接收出错
printf("udpServer socket recv err(%d)\n", errno);
continue;
}
else if (ret == 0)
{
//对方关闭socket
printf("udpServer ret(%d) and client(%d) close!\n", ret, udpSocket);
}
else
{
if (ret < SOCKET_BUFF)
{
memset(&u8RecvBuf[ret], '\0', 1);
}
printf("udpServer client(%d) recv(%s)!\n",udpSocket, u8RecvBuf);
//发送数据到客户端,这里开始处理接收的数据
sendto(udpSocket, JSON_DATA_1, strlen(JSON_DATA_1) + 1, 0, (struct sockaddr *)&sun, u32RemoteAddrLen);
}
}
//关闭服务端
close(udpSocket);
unlink(SERVER_UNIX_SOCKET_FILE);
return;
}
int main()
{
udpServer();
return 0;
}