#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
//使用前应设置端口号 通信时应保持一致
#define DESTHOST 60000
用于发送信息使用的函数接口
注:创建套接字时第二个参数应指定UDP协议指定的宏
/*******************************************************************
*
* 函数名称: sendinfo
* 函数功能: 使用UDP协议向指定IP发送信息的线程
* 函数参数:
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/06/05
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
void *sendinfo(void *arg)
{
// 1.创建udp套接字
int socketfd = socket(AF_INET, // IPV4指定参数
SOCK_DGRAM, // UDP指定参数
0);
if (socketfd == -1) // 错误处理,套接字文件描述符为-1,表示创建失败
{
fprintf(stderr, "udp socket error,errno:%d,%s\n", errno, strerror(errno));
exit(1);
}
// 2.定义数据缓冲区
char sendbuf[128] = {0}; // 用于储存想要发送的数据缓冲区
char timenow[128] = {0}; // 用于储存发送数据的时间缓冲区
char ipbuf[30] = {0}; // 定义缓冲区储存IP地址
struct tm *now = (struct tm *)calloc(1, sizeof(struct tm *)); // 用于记录当前时间的结构体
time_t data; // 记录标准秒数
struct sockaddr_in destaddr; // 定义记录套接字目标主机信息的结构体
destaddr.sin_family = AF_INET; // IPV4指定参数
destaddr.sin_port = htons(DESTHOST); // 服务器端口,必须转换为网络字节序
while (1)
{
printf("请输入想要发送的消息:\n");
data = time(NULL); // 获取标准秒数
now = localtime(&data); // 将标准秒数转换成指定格式存入结构体中
sprintf(timenow, "%04d年%02d月%02d日 %02d:%02d:%02d ", // 将指定格式的时间写入缓冲区中
now->tm_year + 1900,
now->tm_mon + 1,
now->tm_mday,
now->tm_hour,
now->tm_min,
now->tm_sec);
scanf("%s", sendbuf); // 将想要发送的数据存入缓冲区中
strcat(timenow, sendbuf); // 将日期和发送的信息拼接存入timenow缓冲区
printf("%s\n", timenow);
printf("请输入想要通信的IP地址(例:192.168.64.xxx):\n");
scanf("%s", ipbuf); // 从终端输入想要通信的目标主机IP
destaddr.sin_addr.s_addr = inet_addr(ipbuf);
// 向目标主机发送信息
sendto(socketfd, // 套接字文件描述符
timenow, // 想要发送的信息缓冲区
sizeof(timenow), // 缓冲区大小
0, // 发送信息的标志,默认为0
(struct sockaddr *)&destaddr, // UDP指定目标主机IP信息
sizeof(destaddr)); // 主机IP结构体的大小
}
return 0;
}
用于接收信息的函数接口
注:创建套接字时第二个参数应指定UDP协议指定的宏
/*******************************************************************
*
* 函数名称: recvinfo
* 函数功能: 使用UDP协议接收信息的线程
* 函数参数:
* 返回结果:
* 注意事项: None
* 函数作者: [email protected]
* 创建日期: 2024/06/05
* 修改历史:
* 函数版本: V1.0
* *****************************************************************/
void *recvinfo(void *arg)
{
// 1.创建udp套接字
int socketfd = socket(AF_INET, // IPV4指定参数
SOCK_DGRAM, // UDP指定参数
0);
if (socketfd == -1) // 错误处理,套接字文件描述符为-1,表示创建失败
{
fprintf(stderr, "udp socket error,errno:%d,%s\n", errno, strerror(errno));
exit(1);
}
// 2.创建接收数据缓冲区
char recvbuf[128] = {0};
struct sockaddr_in clent; // 定义接收套接字源信的结构体
clent.sin_family = AF_INET; // IPV4指定参数
clent.sin_port = htons(DESTHOST); // 服务器端口,必须转换为网络字节序
clent.sin_addr.s_addr = htonl(INADDR_ANY); // 选择接收所有IP来源的信息
// 3.将套接字与接收结构体进行绑定
bind(socketfd, (struct sockaddr *)&clent, sizeof(struct sockaddr));
struct sockaddr_in sourceaddr;
socklen_t le = sizeof(sourceaddr);
while (1)
{
recvfrom(socketfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)&sourceaddr, &le);
printf("[%s]:%s\n", inet_ntoa(sourceaddr.sin_addr), recvbuf);
bzero(recvbuf, sizeof(recvbuf));
}
}
主函数中的测试程序
两个函数应该同时运行,recvfrom()函数默认会阻塞,并且不应该影响其他任务,所以使用两个线程运行
int main()
{
// 2.创建接收数据的线程
pthread_t recvthread;
pthread_create(&recvthread, NULL, recvinfo, NULL);
// 1.创建发送数据的线程
pthread_t sendthread;
pthread_create(&sendthread, NULL, sendinfo, NULL);
// 3.等待子线程结束并释放资源
pthread_join(sendthread, NULL);
pthread_join(recvthread, NULL);
return 0;
}
编译指令:
gcc "udp_pathname".c -o "udp_pathname" -pthread
运行指令:
./"udp_pathname"
以下为发送消息
以下为指定ip所接收到的信息
注意事项:
1.UDP协议通信是一种面向无连接,不可靠的通信方式,所以在使用前应获得对方IP,以及端口号。
2.使用该协议发送信息,当对方不在线时,会出现丢包情况,所以使用前请确保双方同时在线