C语言实现udp客户端
C语言实现udp客户端,子线程轮询接收数据,QT的界面框架,QMainWindow使用回调函数接收数据,支持windows和linux跨平台。
QMainWindow调用udp
///接收数据回调函数
void udpReceiveMsg(char *data, int32_t nb_data, void *user)
{MainWindow * mw = (MainWindow*)user;if(mw == nullptr) return;mw->UdpRevMsgCallBack(data,nb_data);
}MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);UdpHandle *pUdpHandle = (UdpHandle *)malloc(sizeof(UdpHandle));pUdpHandle->receive = udpReceiveMsg;pUdpHandle->user = this;udp_create(pUdpHandle,(char*)"192.168.72.35",9000,9090);udp_start(pUdpHandle);
}MainWindow::~MainWindow()
{udp_destroy(pUdpHandle);delete ui;
}///接收数据处理
void MainWindow::UdpRevMsgCallBack(char *data, int32_t nb_data)
{QByteArray array(data,nb_data);qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<array;
}///数据发送
void MainWindow::on_pushButton_clicked()
{udp_sendData(pUdpHandle,ui->textEdit->toPlainText().toLatin1().data(),ui->textEdit->toPlainText().size());
}
UdpClient.h
#ifndef UDPHANDLE_H_
#define UDPHANDLE_H_#include
#include #ifdef _WIN32
#include
#include
//#define SOCKADDR_IN
#define socklen_t int
#else#include
#include
#include
#endiftypedef struct {int32_t fd;int32_t localPort;int32_t remotePort;int32_t isStart;int32_t isLoop;int32_t notRemoteInit;pthread_t threadId;//char serverIp[30];void* user;struct sockaddr_in local_addr;struct sockaddr_in remote_addr;void (*receive)(char *data, int32_t nb_data,void* user);void (*startStunTimer)(void* user);
}UdpHandle;#ifdef __cplusplus
extern "C"{
#endif
int32_t udp_create(UdpHandle* udp,char* remoteIp,int32_t remotePort,int32_t plocalPort);//创建udp套接字
void udp_destroy(UdpHandle* udp);//销毁套接字
void udp_start(UdpHandle* udp);//创建线程,启动udp
void udp_stop(UdpHandle* udp);//停止线程处理函数
int32_t udp_sendData(UdpHandle* udp,char* p,int32_t plen);//udp发送数据
#ifdef __cplusplus
}
#endif
#endif
UdpClient.c
#include
#include
#include
#include #include "UdpClient.h"
#ifndef _WIN32
#define GetSockError() errno
#define SetSockError(e) errno = e#define closesocket(s) close(s)
#endif/*** @brief udp_create 创建udp套接字* @param udp udp客户端结构体* @param remoteIp* @param remotePort* @param plocalPort* @return 0成功,-1失败*/
int32_t udp_create(UdpHandle *udp, char *remoteIp,int32_t remotePort,int32_t plocalPort)
{if (udp == NULL) return -1;udp->localPort = plocalPort;udp->fd = -1;udp->remotePort = remotePort;#ifdef _WIN32WORD wVersionRequested;WSADATA wsaData;wVersionRequested = MAKEWORD(2, 2);WSAStartup(wVersionRequested, &wsaData);//初始化socket库
#endifudp->local_addr.sin_family = AF_INET;udp->local_addr.sin_port = htons(udp->localPort);udp->local_addr.sin_addr.s_addr = INADDR_ANY;/*** socket,创建套接字* 参数1,domain:AF_INET,PF_INET代表IPV4,PF_INET6代表TPV6等* 参数2,type:SOCK_STREAM代表TCP连接,SOCK_DGRAM支持UDP连接* 参数3,protocol:用于制定某个协议的特定类型type,某协议中只有一种特定类型则为0* 返回值,成功返回一个标识这个套接字的文件描述符,失败返回-1**/udp->fd = socket(AF_INET, SOCK_DGRAM, 0);if(udp->fd < 0){
#ifdef _WIN32printf("create socket error,udp->fd=%d,WSAGetLastError=%d\n",udp->fd,WSAGetLastError());///WSAGetLastError返回当前错误码,windowsAPI
#elseprintf("create socket error,udp->fd=%d\n",udp->fd);perror("error:");///linuxAPI,打印输出上一个函数发生的错误,"error:"会首先打印
#endifreturn -1;}udp->remote_addr.sin_family = AF_INET;/*** htons,将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian)* 参数1,u_short hostshort: 16位无符号整数* 返回值:TCP / IP网络字节顺序**/udp->remote_addr.sin_port = htons(udp->remotePort);printf("udp->remote_addr.sin_port=%d\n",udp->remote_addr.sin_port);udp->notRemoteInit=remotePort>0?0:1;/*** inet_addr,将一个点分十进制的IP转换成一个长整型数(u_long类型)* 参数1,const char* strptr,传统IP地址* 返回值:成功将字符串转换为32位二进制网络字节序的IPV4地址,失败返回INADDR_NONE**/
#ifdef _WIN32udp->remote_addr.sin_addr.S_un.S_addr=inet_addr(remoteIp);
#elseudp->remote_addr.sin_addr.s_addr = inet_addr(remoteIp);
#endifreturn 0;
}
/*** @brief udp_destroy 销毁套接字* @param udp*/
void udp_destroy(UdpHandle *udp)
{if (udp == NULL)return;udp_stop(udp);if (udp->fd > 0){/*** closesocket,关闭套接字* 参数1,s:套接口描述字* 返回值:成功返回0,失败返回SOCKET_ERROR错误**/closesocket(udp->fd);udp->fd = -1;}
}/*** @brief udp_sendData udp发送数据* @param udp UdpHandle* @param data 数据* @param nb 数据长度* @return 成功则返回实际传送出去的字符数, 失败返回-1*/
int32_t udp_sendData(UdpHandle *udp, char *data, int32_t nb)
{if (udp == NULL || !udp->isStart || udp->notRemoteInit || data==NULL) return -1;return sendto(udp->fd, data, nb, 0, (struct sockaddr*) &udp->remote_addr,sizeof(struct sockaddr)) > 0 ? 0 : 1;/*** sendto,socket发送数据* 参数1,s 为已建好连接的socket* 参数2,msg 为发送的数据内容* 参数3,len为发送的数据长度* 参数4,flags 一般设0* 参数5,to 用来指定欲传送的网络地址* 参数6,tolen 为sockaddr 的结果长度* 返回值:成功则返回实际传送出去的字符数, 失败返回-1**/
}/*** @brief run_udp_thread udp线程处理函数* @param obj UdpHandle* @return*/
void* run_udp_thread(void *obj)
{UdpHandle *udp = (UdpHandle*) obj;udp->isStart = 1;/*** setsockopt,获取或者设置与某个套接字关联的选项* 参数1,sock:将要被设置或者获取选项的套接字。* 参数2,level:选项所在的协议层。* 参数3,optname:需要访问的选项名。* 参数4,tv_out:recv等函数默认为阻塞模式(block),当超过tv_out设定的时间而没有数据到来时recv()就会返回0值* 参数5,optlen:选项的长度* 返回值:成功将字符串转换为32位二进制网络字节序的IPV4地址,失败返回INADDR_NONE**/
#ifdef _WIN32int32_t timeout=200;setsockopt(udp->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timeout, sizeof(timeout));
#elsestruct timeval tv;tv.tv_sec = 0;tv.tv_usec = 200000; // 200 mssetsockopt(udp->fd, SOL_SOCKET, SO_RCVTIMEO, (const char*) &tv, sizeof(struct timeval));
#endifprintf("udp server is starting,localPort=%d\n", udp->localPort);/*** bind,将套接字文件描述符、端口号和ip绑定到一起* 参数1,sockfd 表示socket函数创建的通信文件描述符* 参数2,addr 表示struct sockaddr的地址,用于设定要绑定的ip和端口* 参数3,addrlen 表示所指定的结构体变量的大小* 返回值:成功返回0,失败返回-1**/if (bind(udp->fd, (struct sockaddr*) &udp->local_addr,sizeof(struct sockaddr_in)) < 0){printf("Udp server bind error\n");#ifdef _WIN32printf("WSAGetLastError=%d\n",WSAGetLastError());#elseperror("error:");#endifreturn NULL;}char buffer[2048] = { 0 };udp->isLoop = 1;int32_t len = 0;
// if (!udp->notRemoteInit&&udp->startStunTimer) udp->startStunTimer(udp->user);socklen_t src_len = sizeof(struct sockaddr_in);while (udp->isLoop){struct sockaddr_in src;memset(&src, 0, src_len);memset(buffer, 0, 2048);/*** recvfrom,接收一个数据报并保存源地址* 参数1,s:标识一个已连接套接口的描述字* 参数2,buf:接收数据缓冲区* 参数3,len:缓冲区长度* 参数4,flags:调用操作方式,一般设置为0* 参数5,from:(可选)指针,用来指定欲接收数据的网络地址* 参数6,fromlen:(可选)指针,指向from长度值* 返回值:成功则返回接收到的字符数,失败返回-1**/if ((len = recvfrom(udp->fd, buffer, 2048, 0, (struct sockaddr*) &src, &src_len)) > 0){if(udp->notRemoteInit){udp->remotePort = ntohs(src.sin_port);udp->remote_addr.sin_port = src.sin_port;//htons(udp->remotePort);#ifdef _WIN32udp->remote_addr.sin_addr.S_un.S_addr=src.sin_addr.S_un.S_addr;
#elseudp->remote_addr.sin_addr.s_addr = src.sin_addr.s_addr;
#endifudp->notRemoteInit=0;//if (udp->startStunTimer) udp->startStunTimer(udp->user);}if (udp->receive) udp->receive(buffer, len, udp->user);}}udp->isStart = 0;closesocket(udp->fd);udp->fd = -1;return NULL;
}/*** @brief udp_start 创建线程,启动udp* @param udp UdpHandle*/
void udp_start(UdpHandle *udp)
{if (udp == NULL) return;if (pthread_create(&udp->threadId, 0, run_udp_thread, udp)){printf("start could not start thread\n");}
}/*** @brief 停止线程处理函数* @param udp*/
void udp_stop(UdpHandle *udp)
{if (udp == NULL) return;udp->isLoop = 0;while (udp->isStart)usleep(1000);
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
