1. UDP协议介绍
UDP协议 相对TCP协议来讲属于不可靠协议,UDP协议是广播方式发送数据,没有服务器和客户端的概念。
在Linux下使用socket创建UDP的套接字时,属性要选择数据报类型SOCK_DGRAM
。
sockfd=socket(AF_INET,SOCK_DGRAM,0);
复制代码
2. UDP协议发送和接收数据的函数
2.1 recvfrom函数
UDP使用recvfrom()函数接收数据,他类似于标准的read(),但是在recvfrom()函数中要指明数据的目的地址。
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
复制代码
返回值
成功返回接收到数据的长度,负数失败
前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。最后两个参数类似于accept的最后两个参数(接收客户端的IP地址)。
2.2 sendto函数
UDP使用sendto()函数发送数据,他类似于标准的write(),但是在sendto()函数中要指明目的地址。
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
复制代码
返回值
成功返回发送数据的长度,失败返回-1
前三个参数等同于函数read()的前三个参数,flags参数是传输控制标志。
参数to指明数据将发往的协议地址,他的大小由addrlen参数来指定。
2.3 设置套接字属性
#include /* See NOTES */
#include
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
复制代码
setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
参数介绍:
sockfd:标识一个套接口的描述字。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区的长度。
UDP协议发送数据时,设置具有广播特性: 默认情况下socket不支持广播特性
char bBroadcast=1;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(char));
复制代码
3.案例: UDP协议完成数据收发
3.1 接收数据示例
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
/* According to POSIX.1-2001 */
#include
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("参数: ./tcp_server <端口号>\n");
return 0;
}
/*1. 创建socket套接字*/
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("服务器:套接字创建失败.\n");
return 0;
}
/*2. 绑定端口号*/
struct sockaddr_in addr;
addr.sin_family=AF_INET; //IPV4
addr.sin_port=htons(atoi(argv[1])); //65535
//addr.sin_addr.s_addr=inet_addr("192.168.2.16");
addr.sin_addr.s_addr=INADDR_ANY; //本地所有IP地址 "0.0.0.0"
if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr)))
{
printf("服务器:端口号绑定失败.\n");
return 0;
}
/*3. 接收数据*/
char buff[100];
struct sockaddr_in from;
size_t addrlen=sizeof(struct sockaddr);
ssize_t len;
while(1)
{
//带阻塞功能,收到数据才会返回
len=recvfrom(sockfd,buff,sizeof(buff)-1,0,(struct sockaddr*)&from,&addrlen);
buff[len]='\0';
printf("接收到数据:%s,长度=%d,数据来自于:%s:%d\n",buff,len,inet_ntoa(from.sin_addr),ntohs(from.sin_port));
}
}
复制代码
3.2 发送数据示例
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
/* According to POSIX.1-2001 */
#include
#include
int main(int argc,char **argv)
{
if(argc!=4)
{
printf("参数: ./tcp_client <端口号> <发送的数据>\n");
return 0;
}
/*1. 创建socket套接字*/
int sockfd;
sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
printf("服务器:套接字创建失败.\n");
return 0;
}
const int opt = 1;
//设置该套接字为广播类型,
int nb = 0;
nb = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
if(nb == -1)
{
printf("设置广播类型错误.\n");
}
/*2. 开始发送数据*/
struct sockaddr_in addr;
addr.sin_family=AF_INET; //IPV4
addr.sin_port=htons(atoi(argv[2])); //65535 服务器的端口号
addr.sin_addr.s_addr=inet_addr(argv[1]); //服务器IP地址
ssize_t len;
while(1)
{
len=sendto(sockfd,argv[3],strlen(argv[3]),0,(const struct sockaddr*)&addr,sizeof(struct sockaddr));
printf("成功发送:%d\n",len);
sleep(1);
}
}
复制代码地址>
4. 案例: 使用UDP协议探测在线好友
前面几篇文章介绍了Linux下TCP协议设计的群聊天室的一个程序,如果想要知道同一个网络下有多少好友在线,就可以使用UDP协议进行广播探测。 大家的端口号是固定的,也就是只要在这个网络范围内,大家都跑这个同一个聊天室程序,就可以互相探测,得到对方IP地址之后,再完成TCP协议建立,完成点对点聊天通信。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SEND_MSG "1314520" //发送的数据包
#define PORT 8888 //固定的端口号
int sockfd;
int main(int argc,char **argv)
{
if(argc!=2)
{
printf("./app <广播地址> 当前程序固定的端口号是8888\n");
return 0;
}
/*1. 创建socket套接字*/
sockfd=socket(AF_INET,SOCK_DGRAM,0);
//设置端口号的复用功能
int on = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
/*2. 绑定端口号与IP地址*/
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT); // 端口号0~65535
addr.sin_addr.s_addr=INADDR_ANY; //inet_addr("0.0.0.0"); //IP地址
if(bind(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr))!=0)
{
printf("UDP服务器:端口号绑定失败.\n");
return 0;
}
/*3. 接收数据*/
unsigned char buff[1024+1];
int cnt;
struct sockaddr_in client_addr;
socklen_t addrlen=sizeof(struct sockaddr_in);
struct pollfd fds;
fds.fd=sockfd;
fds.events=POLLIN;
while(1)
{
cnt=poll(&fds,1,1000);
if(cnt>0)
{
cnt=recvfrom(sockfd,buff,1024,0,(struct sockaddr *)&client_addr,&addrlen);
buff[cnt]='\0';
//判断是不是探测包数据
if(strcmp(buff,SEND_MSG)==0)
{
printf("在线好友:%s,%d-->%s:%d\n",buff,cnt,inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
cnt=sendto(sockfd,SEND_MSG,strlen(SEND_MSG),0,(const struct sockaddr *)&client_addr,sizeof(struct sockaddr));
printf("回应探测包:%d字节.\n",cnt);
//这里可以继续写代码,将存在的好友保存在链表,并记录在线好友数量
}
}
else
{
ssize_t cnt;
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT); // 端口号0~65535
addr.sin_addr.s_addr=inet_addr(argv[1]); //IP地址
cnt=sendto(sockfd,SEND_MSG,strlen(SEND_MSG),0,(const struct sockaddr *)&addr,sizeof(struct sockaddr));
printf("探测包发送:%d字节.\n",cnt);
}
}
return 0;
}
审核编辑:汤梓红
-
Linux
+关注
关注
87文章
11276浏览量
209263 -
UDP
+关注
关注
0文章
325浏览量
33929 -
网络编码
+关注
关注
0文章
38浏览量
11599
发布评论请先 登录
相关推荐
评论