Linux网络编程之UDP编程

server/2024/11/17 1:43:34/

        UDP编程效率高,不需要差错校验,在视频点播场景应用高

基于UDP协议客户端和服务端的编程模型,和TCP模型有点类似,有些发送接收函数不同,TCP是之间调用I/O函数read0或write()进行读写操作,而UDP是用sendto()和readfrom()等封装好了的函数进行数据接收发送

1.UDP服务器端:

socket  填写SOCK_DGRAM

bind(),套接字和地址和端口进行绑定

readfrom()获取接收客户端发送过来的报文,是对read()的封装,也是阻塞性的读写,如果接收不到客户端发送过来的数据报文,就阻塞

 sendto()  向对方发送数据报文,

close()关闭套接字

2.客户端:socket()

1.socket()创建套接字

2.bind(),客户端一般不需要绑定

3.sendto()客户端向服务端发送数据报文

4.readfrom()读取服务端返回的数据报文

5.close()关闭套接字

3.数据传输

1.发送数据   成功调用返回发送字节数,出错返回-1

send()函数,

第一个参数,套接字描述符

第二个参数,发送的内容

第三个,第二个参数占的字节内存

第四个通常为0

sendto()函数,比send()多了两个参数,

第五个传入通用地址结构体,里面封装了接收方地址包括端口信息,目的地的信息

第六个参数,第五个参数的大小

sendmsg()    把发送的数据报文封装在叫做msghdr的结构体中

这个结构体第三个参数,可以把具体的数据报文放在里面

2.接收数据

recv()函数

第一个参数,套接字描述符

第二个参数,接收到的内存

第三个参数,第二个参数大小

第四个,一般设置为0

recvfrom()函数

第五个参数传入通用地址结构体,用来存放发送方的一些地址信息,(接收方接收到发送方发过来的数据报文,接收方这方可用这个结构体存放发送方的地址和占用端口,

第六个参数就是第五个参数占用几个字节

recvmsg()函数

将接收的数据报文都存放在msghdr这个结构体当中

4.基于UDP端的服务器编程

                        客户端连接后,服务端返回一个系统时间

1.time_udp_server.c

#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <netdb.h>
#include <signal.h>
#include <arpa/inet.h>//UDP服务器端,返回实时时间
//udp是无连接的,不会做应答和差错校验
int sockfd;void sig_handler(int signo)
{if(signo == SIGINT){printf("server close\n");close(sockfd);exit(1);}
}//输出客户端的一些ip和端口信息
void out_addr(struct sockaddr_in *clientaddr)
{char ip[16];int port;memset(ip, 0, sizeof(ip));//网络字节序转换为点分十进制字节序,是字符串形式inet_ntop(AF_INET, &clientaddr->sin_addr.s_addr, ip, sizeof(ip));port = ntohs(clientaddr->sin_port);printf("client: %s(%d)\n", ip, port);}//负责和客户端进行通讯
void do_service(int fd)
{struct sockaddr_in clientaddr;socklen_t len =sizeof(clientaddr);char buffer[1024];  //缓存memset(buffer, 0, sizeof(buffer));//接收客户端信息,传送的地址结构体用来接收//发送方的地址信息if(recvfrom(sockfd, buffer, sizeof(buffer),0, (struct sockaddr*)&clientaddr, &len) < 0){perror("recvfrom error");}else{out_addr(&clientaddr);//输出客户端的一些相关信息printf("client send into: %s\n",buffer);//向客户端发送数据报文//t为1970年经过的秒数long int t = time(0);//ctime输入秒数,返回现在时间//的字符串char *ptr = ctime(&t);size_t size = strlen(ptr) * sizeof(char);if(sendto(sockfd, ptr, size, 0, (struct sockaddr*)&clientaddr, len) < 0){perror("sendto error");}}}int main(int argc, char *argv[])
{struct timeval timeout;//超时结构体timeout.tv_sec = 10;  // 10 秒timeout.tv_usec = 0;  // 0 微秒if(argc < 2){printf("usage: %s port\n", argv[0]);exit(1);}if(signal(SIGINT, sig_handler) == SIG_ERR){perror("signal sigint error\n");exit(1);}/*步骤1:创建socket*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error\n");exit(1);}/*设置套接字相关选项,服务器停掉,启动服务器端会在指定端口监听,如果服务器断掉监听的端口不会马上使用,把服务器停掉后这些端口能够马上使用,我们最好设置套接字的选项*///udp服务器端设置超时设置int ret;int opt = 1;  //打开//设置套接字的选项,第二个参数表明是通用的套接字层次选项//第三个参数表明设置端口停掉后,下次重新绑定这个端口//这个端口可以重新继续使用if((ret = setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt))) < 0){perror("setsocketopt erroe");exit(1);}//设置接收超时设置10sret = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout));if (ret < 0) {perror("setsockopt SO_RCVTIMEO error");close(sockfd);exit(1);}/*步骤2:调用bind函数对socket和地址进行绑定*/struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET; //IPV4serveraddr.sin_port = htons(atoi(argv[1]));  //端口serveraddr.sin_addr.s_addr = INADDR_ANY;  //获得所有ip请求if(bind(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr))<0){perror("bind error");exit(1);}/*步骤3,和客户端进行双向数据通信*/while(1){do_service(sockfd);}
}

2.time_udp_client.c

#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <netdb.h>
#include <signal.h>
#include <arpa/inet.h>int sockfd;void sig_handler(int signo)
{if(signo == SIGINT){printf("client close\n");close(sockfd);exit(0);}
}//udp端客户端程序
//步骤1创建,绑定,读写,发送和接收数据报文int main(int argc, char *argv[])
{if(argc < 3){printf("usage: %s ip port\n", argv[0]);exit(0);}if(signal(SIGINT, sig_handler) == SIG_ERR){perror("signal error");exit(1);}/*步骤1:创建socket*/sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error");exit(1);}/*步骤2:调用recvfrom和sendto函数和服务器进行双向通信*///定义服务端的结构体地址struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));//点分十进制转换为网络字节序inet_pton(AF_INET, argv[1], &serveraddr.sin_addr.s_addr);//客户端向服务端发送数据报文//要向服务器发送的内容//在udp编程中,能否调用connect//在tcp中调用connect经过三次握手//在udp中调用connect后内核中记录了服务器端//的地址信息,包括了服务器端ip和端口//调用了connect后,可以用send(),而不是sendto()//if(connect(sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){perror("connect error");exit(1);}char buffer[1024] = "hello iotek";//if(sendto(sockfd, buffer, sizeof(buffer), 0//    , (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0){//调用了connect内核中记录了服务器的地址和端口,发送时会自动发给内核记录地址if(send(sockfd, buffer, sizeof(buffer), 0) < 0){perror("sendto error");exit(1);}else{//如果成功发送,接收服务器发送报文memset(buffer, 0, sizeof(buffer));size_t size;//开始接收服务方发送返回信息//recv和recvfrom()都是阻塞性的//调用connect成功后,接收会接收connect好的地址的报文if((size = recv(sockfd, buffer,sizeof(buffer), 0)) < 0){perror("recv error");exit(1);}else{ //成功接收,显示到标准输出printf("%s\n", buffer);}}return 0;
}

运行结果:

服务端

客户端:

3.设置选项

设置了这个选项,服务端可以重新绑定这个端口,上一次绑定的就作废,客户端连接到服务端的时候之连接到重新绑定的服务器,上一次绑定的没有反应

 int ret;int opt = 1;  //打开//设置套接字的选项,第二个参数表明是通用的套接字层次选项//第三个参数表明设置端口停掉后,下次重新绑定这个端口//这个端口可以重新继续使用if((ret = setsockopt(sockfd, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt))) < 0){perror("setsocketopt erroe");exit(1);}

5.域名解析

访问一些网站,www.google.com就是域名都是与IP进行绑定的,当通过浏览器输入域名以后,服务器端会根据域名解析出对应绑定的IP地址

在域名解析服务器(就是DNS)上面注册了很多域名和其对应的IP地址,客户端发送网址如www.google.com(就是域名),来到DNS域名解析服务器上,自动解析出绑定的IP地址,DNS域名服务器返回IP地址给客户端,客户端就可以访问到真正的IP地址

对域名的解析,可以通过相应的函数来完成

1.域名解析函数和结构体

在本机操作,将域名和ip地址绑定,输入域名,可以解析出ip地址

h_name:正式主机名  字符串,只有一个

h_aliases:别名   放在字符串数组里面,可以有多个

h_addrtype:协议类型

gethostbyname()传进去的是主机名,返回的是hostent结构体地址,里面包含了hostname对应的ip地址 

endhostent()对域名的结构体进行释放,和gethostbyname()配合使用

more /etc/hosts    查看ubuntu下域名和ip地址的映射关系

下面是一个简单的域名解析案例

6,UDP编程案例——域名解析

vi /etc/hosts  编辑对域名相关的    IP和绑定的别名

gethostbyname()仅仅支持IPV4,不支持IPV6

gethost.c

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>//DNS域名解析,与hostent结构体有关void out_addr(struct hostent *h)
{//主机名字printf("hostname: %s\n", h->h_name);//协议类型printf("addrtype: %s\n", h->h_addrtype == AF_INET ? "ipv4" : "ipv6");char ip[16];memset(ip, 0, sizeof(ip));//把ip地址网络字节序转换为点分十进制的字节序inet_ntop(h->h_addrtype, h->h_addr_list[0],ip, sizeof(ip));printf("ip address:%s\n", ip);int i = 0;//输出别名while(h->h_aliases[i] != NULL){  //别名printf("aliase: %s\n", h->h_aliases[i]);i++;}
}int main(int argc, char *argv[])
{if(argc < 2){printf("usage:%s host\n",argv[0]);exit(1);}struct hostent *h;//根据主机名或别名得到域名解析函数hostent的结构体//gethostbyname一般不支持IPV6类型,一般只支持IPV4h = gethostbyname(argv[1]);if(h != NULL){out_addr(h);  }else{perror("gethost error");exit(1);}}


http://www.ppmy.cn/server/142522.html

相关文章

产品思维如何颠覆我的开发与盈利观-营销自己

之前&#xff0c;我独自一人开发了一个名为“心情追忆”的小程序&#xff0c;旨在帮助用户记录日常的心情变化及重要时刻。从项目的构思、设计、前端&#xff08;小程序&#xff09;开发、后端搭建到最终部署&#xff0c;所有环节都由我一人包办。经过一个月的努力&#xff0c;…

基于图像处理与机器学习的车牌检测识别系统设计与实现

摘要&#xff1a;随着智能交通系统的快速发展&#xff0c;车牌检测识别技术在交通管理、安防监控等领域的应用日益广泛。然而&#xff0c;复杂环境因素如光照变化、遮挡、背景干扰等给车牌检测识别带来诸多挑战。本研究旨在设计并实现一种鲁棒性强、准确率高的车牌检测识别系统…

【提高篇】3.4 GPIO(四,工作模式详解 下)

五,模拟输入输出 5.1 模拟功能 上下拉电阻断开,施密特触发器关闭,双 MOS 管也关闭。该模式用于 ADC 采集或者 DAC 输出,或者低功耗下省电。但要注意的是 GPIO本身并不具备模拟输出输入的功能。 5.2 模拟功能的特点 上拉电阻关闭下拉电阻关闭施密特触发器关闭双MOS管不…

Docker实践与应用举例:构建高效开发与部署环境

Docker实践与应用举例&#xff1a;构建高效开发与部署环境 在当今快速迭代的软件开发领域&#xff0c;容器化技术以其轻量级、可移植性和高效资源利用的特点&#xff0c;成为了现代应用开发和部署不可或缺的工具。Docker作为容器技术的佼佼者&#xff0c;不仅简化了应用程序的…

C++之红黑树

红黑树的概念 红黑树是一种二叉搜索树&#xff0c;在每个节点上增加一个储存位代表节点的颜色&#xff0c;可以是黑色或者是红色。通过对任何一条从根节点到叶子节点的路径上节点颜色的限制&#xff0c;红黑树可以保证没有一条路径会比其他路径长两倍&#xff0c;由此接近平衡…

python爬虫初体验(五)—— 边学边玩小游戏

1. 打开浏览器 利用webbrowser 模块的 open()函数可以启动一个新浏览器&#xff0c;打开指定的 URL。 import webbrowser webbrowser.open(http://inventwithpython.com/) 2. 猜数字游戏 # -*- coding: utf-8 -*- # This is a guess the number game. import randomsecretN…

构建Spring Boot编程训练系统:全面指南

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理编程训练系统的相关信息成为必然。开发合适…

STM32芯片EXIT外部中断的配置与原理以及模板代码(标准库)

配置EXIT外部中断其实就是把GPIO刀NVIC的各个外设配置好 第一步&#xff1a;配置RCC&#xff0c;把我们涉及到的外设的时钟都打开 &#xff08;此处EXTI是默认打开的&#xff0c;而NVIC是内核外设无需配置&#xff09; 第二步&#xff1a;配置GPIO,选择端口为输入模式 第三…