您的位置:首页 > 财经 > 金融 > 跨境电商自建站平台_十大黄冈网站排行榜_线上营销方式_网络推广一个月的收入

跨境电商自建站平台_十大黄冈网站排行榜_线上营销方式_网络推广一个月的收入

2025/3/28 8:47:18 来源:https://blog.csdn.net/2302_78417897/article/details/146438063  浏览:    关键词:跨境电商自建站平台_十大黄冈网站排行榜_线上营销方式_网络推广一个月的收入
跨境电商自建站平台_十大黄冈网站排行榜_线上营销方式_网络推广一个月的收入

一、概述

UDP(User Datagram Protocol) : 用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可用于进行高效率的传输。 但不保证数据的可靠性

特点
UDP 是无连接的协议
UDP 不保证数据可靠性
UDP 是面向报文的
UDP通信的实时性较高

缺点:没有流控制,没有应答确认机制,不能解决丢包、重发、错序问题。

使用场景
适合于广播/组播式通信中。
MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议
流媒体、VOD、VoIP、IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输

在这里插入图片描述

二、UDP协议报文

用户数据报由两个部分组成:首部 + 数据部分。首部部分很简单,只有 8 个字节,由四个字段组成,每个字段的长度都是两个字节。

在这里插入图片描述
源端口号: 源端口号,需要对方回信时选用,不需要时全部置0.
目的端口号:目的端口号,在终点交付报文的时候需要用到。
长度:UDP的数据报的长度(包括首部和数据)其最小值为8(只有首部)
校验和:检测UDP数据报在传输中是否有错,有错则丢弃

UDP和TCP协议对比
特点
tcp协议是面向连接、可靠、基于字节流
udp协议是无连接、不可靠、基于数据报文

性能
tcp协议传输效率慢,所需要资源多
udp协议传输效率快,所需要资源少

应用常用
tcp协议常用于文件,邮件传输
udp协议常用于语音,视频,直播等实时性要求较高的场所

三、C语言中UDP实现

常见服务器类型:
处理多客户端的通讯处理方式不同,就导致服务器分为以下几种类型

  1. 迭代服务器
    大多数UDP都是迭代运行,服务器等待客户端的数据,收到数据后处理该数据,送回其应答,在等待下一个客户端请求。

在这里插入图片描述

  1. 并发服务器
    并发服务器是指在同一个时刻可以响应多个客户端的请求
    本质是创建 多进程/多线程 ,对多数用户的信息进行处理
    UDP协议一般默认是不支持多线程并发的 ,因为默认UDP服务器只有一个sockfd,所有的客户端都是通过同一个sockfd进行通信的。udp使用一个socket,如何做到做并发呢?(子进程/子线程中重新创建

(一)、UDP并发服务器之多进程并发

  1. 场景设计
    多个udp客户端需要先验证密钥是否正确后,才允许进行数据交互。假设密钥为"root"。(类似于登录功能)
    服务器接收到客户端信息,需要考虑两种情况
    <1>A用户的密钥验证请求消息
    <2>B用户的数据交互接收消息

  2. 框架图
    在这里插入图片描述

  3. 使用场景
    当UDP服务器与客户端交互多个数据报。问题在于每个客户都是往服务器端的同一个的端口发送数据,并用的同一个sockfd。并发服务器的每一个子进程如何正确区分每一个客户的数据报(涉及到进程的调度问题,如何避免一个子进程读取到不该它服务的客户发送来的数据报)。
    解决的方法是服务器(知名端口)等待客户的到来,当一个客户到来后,记下其IP和port,然后服务器fork一个子进程,建立一个socket再bind一个随机端口,然后建立与客户端的连接,并处理该客户的请求。父进程继续循环,等待下一个客户的到来。 在tftpd中就是使用这种技术的 。

代码实现

UDP服务器代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#define LOGIN_KEY "root"
#define LOGIN_SUCCESS 1
#define LOGIN_FAILURE 0
int init_socket(const char* ip,const char* port)
{
// 创建socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1){
perror("socket");
return -1;}
// 绑定IP + 端口
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
addr.sin_family =AF_INET;
addr.sin_port = htons(atoi(port));
inet_aton(ip,&(addr.sin_addr));
int addrlen = sizeof(struct sockaddr_in);
if(bind(sockfd,(struct sockaddr*)&addr,addrlen)==-1){
fprintf(stderr,"bind failed\n");
return -1;}
return sockfd;
}
int authentication_key(const char* ip,const char* port)
{
int sockfd = init_socket(ip,port);
if(sockfd == -1){
return -1;}
char buf[512]={0};
int len = sizeof(buf);
struct sockaddr_in addr;
int addrlen = sizeof(addr);
int new_sockfd;
// 循环验证用户密钥
while(1)
{
memset(buf,0,len);
ssize_t recvbytes = recvfrom(sockfd,buf,len,0,(struct
sockaddr*)&addr,&addrlen);
if(recvbytes == -1){
perror("redvfrom");
return -1;}
unsigned char loginstatus = (strncmp(buf,LOGIN_KEY,4)==0)?
LOGIN_SUCCESS:LOGIN_FAILURE;
if(loginstatus == LOGIN_SUCCESS){
pid_t pid = fork();
if(pid == -1){
perror("fork");
return -1;}
else if(pid == 0){
// 执行的是子进程
close(sockfd);//密钥验证成功,不需要sockfd文件描述符
new_sockfd = init_socket(ip,"0");// 绑定0端口,系统会随机分配一个可用的端口号
sendto(new_sockfd,&loginstatus,sizeof(loginstatus),0,(struct
sockaddr*)&addr,addrlen);
break;}}
else{
// 登录失败,使用原端口回复信息
ssize_t ret =
sendto(sockfd,&loginstatus,sizeof(loginstatus),0,(struct sockaddr*)&addr,addrlen);}}
return new_sockfd;
}
// 接收数据
void recv_data(int sockfd)
{
char buf[512]={0};
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
while(1){
memset(buf,0,sizeof(buf));
ssize_t recvbytes = recvfrom(sockfd,buf,sizeof(buf),0,(struct
sockaddr*)&client_addr,&addrlen);
if(recvbytes== -1){
perror("recvfrom");
exit(EXIT_FAILURE);}
printf("client ip:%s\n",inet_ntoa(client_addr.sin_addr));
printf("client port:%d\n",ntohs(client_addr.sin_port));
printf("client content:%s\n",buf);
if(strncmp(buf,"quit",4)==0){
break;}}
close(sockfd);
exit(EXIT_SUCCESS);
}
void signal_handler(int signum)
{
// 回收子进程的资源
waitpid(-1,NULL,WNOHANG);
printf("%s\n",strsignal(signum));
}
int main(int argc,char* argv[])
{
if(argc !=3){
fprintf(stderr,"%s ip port.\n",argv[0]);
exit(EXIT_FAILURE);}
// 回收僵尸态的子进程[子进程结束后,会发SIGCHLD信号]
if(signal(SIGCHLD,signal_handler)==SIG_ERR){
perror("signal error.");
exit(EXIT_FAILURE);}
// 验证秘钥
int sockfd = authentication_key(argv[1],argv[2]);
// recv data
recv_data(sockfd);
return 0;
}

UDP客户端代码实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define LOGIN_SUCCESS 1
#define LOGIN_FAILURE 0
// 发送交互消息
void send_message(int sockfd,struct sockaddr_in* addr,int addrlen)
{
char buf[512]={0};
while(1){
memset(buf,0,sizeof(buf));
printf("client:");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
int ret = sendto(sockfd,buf,strlen(buf),0,(struct
sockaddr*)addr,addrlen);
if(ret == -1){
perror("client sendto");
exit(EXIT_FAILURE);}
if(strncmp(buf,"quit",4)==0){
break;}}
}
void send_authentication_key(int sockfd,struct sockaddr_in* addr,struct
sockaddr_in* server_data_addr,int addrlen)
{
char buf[512]={0};
unsigned char flag = LOGIN_FAILURE;
while(1){
putchar('>');
memset(buf,0,sizeof(buf));
fgets(buf,sizeof(buf),stdin); //接收用户的输入
buf[strlen(buf)-1]='\0';
int ret = sendto(sockfd,buf,strlen(buf),0,(struct
sockaddr*)addr,addrlen);
if(ret == -1){
perror("client sendto");
exit(EXIT_FAILURE);
}
// 接收服务器返回的消息
ssize_t recvbytes = recvfrom(sockfd,&flag,sizeof(flag),0,(struct
sockaddr*)server_data_addr,&addrlen);
if(recvbytes == -1){
perror("client recvfrom");
exit(EXIT_FAILURE);}
if(flag == LOGIN_SUCCESS){
break;}}
}
int main(int argc,char* argv[])
{
if(argc !=3){
fprintf(stderr,"%s ip port.\n",argv[0]);
exit(EXIT_FAILURE);}
// 创建socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1){
perror("socket");
return -1;}
// 服务器端的地址
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
addr.sin_family =AF_INET;
addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1],&(addr.sin_addr));
int addrlen = sizeof(struct sockaddr_in);
// 处理数据的服务端信息
struct sockaddr_in server_data_addr;
memset(&server_data_addr,0,sizeof(struct sockaddr_in));
server_data_addr.sin_family =AF_INET;
// 发送密钥信息
send_authentication_key(sockfd,&addr,&server_data_addr,addrlen);
// 发送交互消息
send_message(sockfd,&server_data_addr,addrlen);
//关闭文件描述符
close(sockfd);
return 0;
}

在这里插入图片描述

(二)、UDP并发服务器之多线程并发

多线程并发服务器和多进程并发服务器的思路大同小异,都是服务器端等待客户端,当一个客户端到来后,记录其IP和port,然后同理,创建子线程,建立一个socket再bind一个随机端口,然后建立与客户端的连接,并处理该客户端的请求。父线程继续循环,等待下一个客户端的到来。 一个进程是可以绑定多个端口。

  1. 框架图
    在这里插入图片描述
  2. 代码实现
    服务端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#define LOGIN_KEY "root"
#define LOGIN_SUCCESS 1
#define LOGIN_FAILURE 0
typedef struct{
char ip[256];// ip
unsigned char flag; // 登录标识
struct sockaddr_in addr;// 数据交互的地址
}packet_t;
int init_socket(const char* ip,const char* port)
{
// 创建socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd == -1){
perror("socket");
return -1;}
// 绑定IP + 端口
struct sockaddr_in addr;
memset(&addr,0,sizeof(struct sockaddr_in));
addr.sin_family =AF_INET;
addr.sin_port = htons(atoi(port));
inet_aton(ip,&(addr.sin_addr));
int addrlen = sizeof(struct sockaddr_in);
if(bind(sockfd,(struct sockaddr*)&addr,addrlen)==-1){
fprintf(stderr,"bind failed\n");
return -1;}
return sockfd;
}
void* message_handle(void* argv)
{
void recv_data(int sockfd);
// 接收参数
packet_t* packet = (packet_t*)argv;
//创建新的socket
int new_sockfd = init_socket(packet->ip,"0");
if(new_sockfd == -1){
perror("init_socket");
return NULL;}
// 给客户端响应登录成功
sendto(new_sockfd,&(packet->flag),sizeof(packet->flag),0,(struct
sockaddr*)&(packet->addr),sizeof(struct sockaddr));
// 数据交互
recv_data(new_sockfd);
pthread_exit(NULL);
}
void authentication_key(const char* ip,const char* port)
{
int sockfd = init_socket(ip,port);
if(sockfd == -1){
return -1;}
char buf[512]={0};
int len = sizeof(buf);
struct sockaddr_in addr;
int addrlen = sizeof(addr);
int new_sockfd;
pthread_t pid;
// 循环验证用户密钥
while(1){
memset(buf,0,len);
ssize_t recvbytes = recvfrom(sockfd,buf,len,0,(struct
sockaddr*)&addr,&addrlen);
if(recvbytes == -1){
perror("redvfrom");
return -1;}
unsigned char loginstatus = (strncmp(buf,LOGIN_KEY,4)==0)?
LOGIN_SUCCESS:LOGIN_FAILURE;
if(loginstatus == LOGIN_SUCCESS){
// 创建子线程
packet_t packet;
strcpy(packet.ip,ip);
packet.flag = loginstatus;
packet.addr = addr;
pthread_create(&pid,NULL,message_handle,&packet);
// 线程分离
pthread_detach(pid);}
else{
// 登录失败,使用原端口回复信息
ssize_t ret =
sendto(sockfd,&loginstatus,sizeof(loginstatus),0,(struct sockaddr*)&addr,addrlen);}}
}
// 接收数据
void recv_data(int sockfd)
{
char buf[512]={0};
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof(client_addr);
while(1){
memset(buf,0,sizeof(buf));
ssize_t recvbytes = recvfrom(sockfd,buf,sizeof(buf),0,(struct
sockaddr*)&client_addr,&addrlen);
if(recvbytes== -1){
perror("recvfrom");
exit(EXIT_FAILURE);}
printf("client ip:%s\n",inet_ntoa(client_addr.sin_addr));
printf("client port:%d\n",ntohs(client_addr.sin_port));
printf("client content:%s\n",buf);
if(strncmp(buf,"quit",4)==0){
break;}}
close(sockfd);
}
int main(int argc,char* argv[])
{
if(argc !=3){
fprintf(stderr,"%s ip port.\n",argv[0]);
exit(EXIT_FAILURE);}
// 验证秘钥
authentication_key(argv[1],argv[2]);
return 0;
}

客户端代码与进程那块一样

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com