UDP编程

kehUDP编程—QQ聊天室

概述:

UDP是User Datagram Protocol --用户数据报协议,是一个简单的面向数据报的运输层协议,是一种无连接的协议。UDP不提供可靠性的传输,它只是把应用程序传给IP层的数据报发送出去,但不保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个联系,且没有超时重发等机制,故而传输速度很快。d

特点:

  • 邮件系统服务模式的抽象
  • 每个分组都携带完整的目的地址
  • 发送数据之前不需要建立连接
  • 不对数据包的顺序进行检查,不能保证分组的先后顺序
  • 不进行分组出错的恢复和重传
  • 不保证数据传输的可靠性

在网络质量不好的情况下,UDP协议数据包丢失会比较严重。但是由于UDP的特性:

它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频,视频和普通数据在传送时使用 UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接受结果产生太大影响。比如我们聊天用的ICQ和QQ就使用的UDP协议

UDP编程的C/S架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KF3AV12z-1656939566349)(D:\图片\image-20220704180859668.png)]

对比于写信模型。客户端相当于寄信人,要想成功给人寄信,信封上必须写上对方的地址

UDP客户端程序

socket创建通信的套接字

#include<sys/socket.h>
int socket(int domain,int type,int protocol);
参数:domain协议族:协议AF_INET IPv4   AF_INET6  IPv6
type类型:SOCK_DGRAM(UDP的套接字),SOCK_STREAM(TCP套接字)SOCK_RAW(原始套接字)
protocol协议类别:(0,IPPROTO_TCP,IPPROTO_UDP)
返回值:
     >0 通信的文件描述符
     <0 创建失败

sendto()发送

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd,//套接字
const void*buf,//发送数据缓冲区
size_t nbytes,//发送数据缓冲区的大小
int flags,//一般为0
const struct sockaddr*to,//指向目的主机地址结构体的指针
socklen_t addrlen)//to 所指向内容的长度
功能:向to结构体指针中指定的ip,发送UDP数据,可以发送0长度的UDP数据包
返回值:
     成功:发送数据的长度
     失败:-1

IPv4地址结构

存放IPv4协议通信的所有地址信息

#include<netinet/in.h>
struct sockaddr_in {
sa_family_t sin_family;//2 字节 协议AF_INET AF_INET6
in_port_t sin_port;//2 字节 端口
struct in_addr sin_addr;//4 字节 IP地址(32位无符号整数)
char sin_zero[8]//8 字节 全写0
};
struct in_addr{
in_addr_t s_addr;//4 字节
};

通用地址结构(类型转换)

struct sockaddr{
sa_family_t sa_family;//2字节
char sa_data[14]//14字节
}

DUP客户端编程流程,有点类似于写信的过程:

找个邮政工作人员(socket())-----信封上写上地址同时里面装上信件内容并且投递(sendto())------对方接受(recvform())-----收工(close()

recvfrom()收到

ssize_t recvfrom(int sockfd,//套接字
void *buf,//接受数据缓冲区
size_t nbytes,//接受数据缓冲区的大小
int flags,//套接字标志(常用0)
struct sockaddr*from,//源地址结构体指针,用来保存数据的来源
socklen_t*addrlen)//from所指内容的长度
功能:接受UDP数据,并将源地址信息保存在from指向的结构中,默认的情况下,如果没有接受到数据,这个函数会阻塞,直到有数据到来

客户端程序发送接受

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
        unsigned short port=8080;//服务器端口
        char*server_ip="192.168.1.101";//服务器ip地址
        int sockfd;
        sockfd=socket(AF_INET,SOCK_DGRAM,0);//创建UDP套字
        //udp客户端 发送消息 给服务器
        //定义一个IPv4地址结构 存放服务器的地址信息(目的主机)
        struct sockaddr_in ser_addr;
        memset(&ser_addr,0,sizeof(ser_addr));
        ser_addr.sin_family=AF_INET;
        ser_addr.sin_port=htons(port);//服务器端口
        //服务器的ip地址
    inet_pton(AF_INET,server_ip,&ser_addr.sin_addr.s_addr);
        //发送数据
        sendto(sockfd,"hello word",strlen("hello word"),0,(struct sockaddr*)&ser_addr,sizeof(ser_addr));
           //接收数据

        //定义一个IPv4地址结构 存放发送者的信息
while(1)
{
struct sockaddr_in from_addr;
socklen_t from_len=sizeof(from_addr);
unsigned char buf[1500]="";
int len=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&from_addr,&from_len);
char ip[16]="";
inet_ntop(AF_INET,&from_addr.sin_addr.s_addr,ip,16);
printf("消息来自于%s %hu---->",ip,ntohs(from_addr.sin_port));
printf("len:%d  msg:%s\n",len,buf);
}
    //关闭套接字
        close(sockfd);
        return 0;
}

UDP注意事项:

  • 本地ip,本地端口(我是谁)
  • 目的ip,目的端口(发给谁)
  • 在客户端的代码中,我们只设置了目的ip,目的端口
  • 客户端的本地IP,本地port是我们调用sendto的时候Linux系统底层自动给客户端分配的;分配端口的方式为随机分配,及每次运行系统给的port不一样

bind给udp套接字绑定固定的port,ip信息

#include<sys/socket.h>
int bind(int socket,const struct sockaddr*address)
socklen_t address_len
返回值:
    成功:0
    失败为-1

UDP编程–QQ

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<pthread.h>
#include<arpa/inet.h>
#include<unistd.h>
void *send_function(void *arg)
{
        //获取套接字
        int sockfd=*(int*)arg;
        //定义目的地址
        struct sockaddr_in  dst_addr;
        bzero(&dst_addr,sizeof(dst_addr));
        dst_addr.sin_family=AF_INET;
        while(1)
        {
                //获取键盘输入
                char buf[128]="";
                fgets(buf,sizeof(buf),stdin);
                buf[strlen(buf)-1]=0;
                //判断是否是ip port
                //sayto ip port
                if(strncmp(buf,"sayto",5)==0)
                {
                        char ip[16]="";
                        unsigned short port=0;
                        sscanf(buf,"sayto %s %hu",ip,&port);
     dst_addr.sin_port=htons(port);
                        inet_pton(AF_INET,ip,&dst_addr.sin_addr.s_addr);
                        continue;
                }
                else
                {
                        sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&dst_addr,sizeof(dst_addr));
                        if(strcmp(buf,"bye")==0)
                        {
                                break;
                        }
                }

        }
        return NULL;
}
void *recv_function(void *arg)
{
int sockfd=*(int*)arg;
while(1)
{
        struct sockaddr_in from_addr;
        socklen_t from_len=sizeof(from_addr);
        unsigned char buf[1500]="";
        char ip[16]="";
        int len=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&from_addr,&from_len);
        printf("%s %hu:%s\n",inet_ntop(AF_INET,&from_addr.sin_addr.s_addr,ip,16),ntohs(from_addr.sin_port),buf);
        if(strcmp(buf,"bye")==0)
        {
                break;
        }
}
        return NULL;


}
int main(int argc,char const*argv[])
{
//判断参数,./a.out 8080
if(argc!=2)
{
        printf("./a.out 8080\n");
        return 0;
}
//创建udp套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
//bind绑定固定的端口,ip
struct sockaddr_in my_addr;
bzero(&my_addr,sizeof(my_addr));
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(atoi(argv[1]));
my_addr.sin_addr.s_addr=htonl(INADDR_ANY);
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
//创建发送线程
pthread_t send_tid;
pthread_create(&send_tid,NULL,send_function,&sockfd);
//创建接受线程
pthread_t recv_tid;
pthread_create(&recv_tid,NULL,recv_function,&sockfd);

pthread_join(send_tid,NULL);
pthread_join(recv_tid,NULL);
//关闭套接字
close(sockfd);
return 0;
}

ead_t send_tid;
pthread_create(&send_tid,NULL,send_function,&sockfd);
//创建接受线程
pthread_t recv_tid;
pthread_create(&recv_tid,NULL,recv_function,&sockfd);

pthread_join(send_tid,NULL);
pthread_join(recv_tid,NULL);
//关闭套接字
close(sockfd);
return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值