1 UDPの概念
UDPとはUser Datagram Protocolの略で、中国語名はUser Datagram Protocolで、OSI(Open System Interconnection、Open System Interconnection)参照モデルにおけるコネクションレス型のトランスポート層プロトコルです。
UDP プロトコルは、TCP プロトコルと同様にデータ パケットの処理に使用されますが、OSI モデルでは、両方とも IP プロトコルの上位層であるトランスポート層に位置します。UDP には、データ パケットのグループ化、組み立て、並べ替えができないという欠点があります。つまり、メッセージが送信された後、メッセージが安全かつ完全に到着したかどうかを知ることが不可能です。UDP は、コンピュータ間でデータを転送する必要があるネットワーク アプリケーションをサポートするために使用されます。ネットワーク ビデオ会議システムを含む多くのクライアント/サーバー ネットワーク アプリケーションでは、UDP プロトコルを使用する必要があります。UDP プロトコルは、その誕生以来長年にわたって使用されてきましたが、その本来の輝きはいくつかの同様のプロトコルによって影が薄くなっていますが、今日でも UDP は非常に実用的で実現可能なネットワーク トランスポート層プロトコルです。
多くのアプリケーションは、マルチメディア データ フローなどの UDP のみをサポートしており、追加のデータを生成せず、破損したパケットがあることがわかっていても再送信しません。オーディオやマルチメディア アプリケーションなど、伝送の整合性よりも伝送パフォーマンスを重視する場合は、UDP が最適な選択です。UDP は、データ送信時間が非常に短く、前の接続プロセスがトラフィック全体の主体となる場合にも適しています。
2 UDP プログラミングの主な機能と手順
1ソケット
int socket(int domain, int type, int protocol);
功能:创建一个用来进程通信的套接字,返回文件描述符
参数:
domain:AF_INET IPv4协议族
type:SOCK_STREAM 流式套接字 tcp传输协议
SOCK_DGRAM 数据报套接字 udp传输协议
SOCK_RAW 原始套接字
protocol:默认为0
返回值:
成功返回套接字新文件描述符
失败返回-1
2 バインド
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将套接字与IP地址端口绑定在一起
参数:
sockfd:文件描述符
addr:结构体空间首地址
addrlen:信息的长度
返回值:
成功返回0
失败返回-1
3 2 つ送信します
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:给另一个套接字发送消息
参数:
sockfd:套接字文件描述符
buf:要发送数据存放空间的首地址
len:要发送数据的长度
flags:发送属性 默认为0
dest_addr:目的地址
addrlen:目的地址信息长度
返回值:
成功返回发送字节个数
失败返回-1
4 受信から
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据
参数:
sockfd:套接字文件描述符
buf:存放接收到数据空间的首地址
len:最多允许接收的字节数
flags:属性 默认为0
src_addr:存放发送端地址信息空间首地址
addrlen:想要接收发送端地址大小的变量空间首地址
返回值:
成功返回实际接收字节数
失败返回-1
**注意:该函数具有阻塞功能**
5 htons と ntohs
uint16_t htons(uint16_t hostshort);
功能:将本地字节序转换为网络字节序
参数:hostshort:本地端口号
返回值:返回网络字节序端口号
uint16_t ntohs(uint16_t netshort);
功能:将网络字节序转换为本地字节序
参数:netshort:网络端口号
返回值:返回本地字节序端口号
6ソケット構造
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
3 UDP プログラミングを使用して、クライアントとサーバーのポーリング間の単一の対話を実現します。
この写真では私の IP アドレスは 192.168.1.117 なので、このアドレスは以下のすべてのコードで使用されています。
ヘッドファイル
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/epoll.h>
#endif
クライアントコード
#include "head.h"
int main(int argc, char const *argv[])
{
// 初始化结构体套接字
struct sockaddr_in cli;
struct sockaddr_in ser = {
.sin_family = AF_INET,
.sin_port = htons(50000),
.sin_addr.s_addr = inet_addr("192.168.1.117"),//这里你需要改为自己的ip
};
//==1==
int sock_fd_a = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sock_fd_a)
{
perror("fail to socket");
return -1;
}
while (1)
{
//==2==
char sendbuf[1024] = {
0};
fgets(sendbuf, sizeof(sendbuf),stdin);
ssize_t nret1 = sendto(sock_fd_a, sendbuf, strlen(sendbuf) + 1, 0, (struct sockaddr *)&ser, sizeof(ser));
//==3==
char recbuf[1024] = {
0};
ssize_t nret2 = recvfrom(sock_fd_a, recbuf, sizeof(recbuf), 0, NULL, NULL);
printf("ser->cli:%s", recbuf);
}
//==4==
close(sock_fd_a);
return 0;
}
クライアントコード
#include "head.h"
int main(int argc, char const *argv[])
{
// 初始化结构体套接字
struct sockaddr_in cli;
struct sockaddr_in ser = {
.sin_family = AF_INET,
.sin_port = htons(50000),
.sin_addr.s_addr = inet_addr("192.168.1.117"),
};
//==1==
int sock_fd_b = socket(AF_INET, SOCK_DGRAM, 0);
if (-1 == sock_fd_b)
{
perror("fail to socket");
return -1;
}
//==2==
int bret = bind(sock_fd_b, (struct sockaddr *)&ser, sizeof(ser));
if (-1 == bret)
{
perror("fail to bind");
return -1;
}
while (1)
{
//==3==
char recbuf[1024] = {
0};
socklen_t serlen = sizeof(ser);
ssize_t nret2 = recvfrom(sock_fd_b, recbuf, sizeof(recbuf), 0, (struct sockaddr *)&ser, &serlen);
printf("cli->ser:%s", recbuf);
//==4==
char sendbuf[1024] = {
0};
fgets(sendbuf, sizeof(sendbuf),stdin);
ssize_t nret1 = sendto(sock_fd_b, sendbuf, strlen(sendbuf) + 1, 0, (struct sockaddr *)&ser, sizeof(ser));
}
close(sock_fd_b);
return 0;
}
4 UDP プログラミングを使用して、クライアントとサーバー間のファイル転送を実現します (ここでは画像ファイルが使用されており、ヘッダー ファイルは上記のヘッダー ファイルと同じです。UDP のパケット損失現象は正常であり、わずかな遅延が発生する可能性があります)改善するために追加します)
クライアント (前のブログで説明した、ファイル IO の知識を使用します)
#include "head.h"
int main(int argc, char const *argv[])
{
struct stat file_stat;
struct sockaddr_in cli;
struct sockaddr_in ser = {
.sin_family = AF_INET,
.sin_port = htons(50000),
.sin_addr.s_addr = inet_addr("192.168.1.117"),
};
int fd_src = open("./src.jpg",O_RDONLY);//打开文件
if(-1 == fd_src)
{
perror("fail to open");
return -1;
}
int stat_ret = fstat(fd_src,&file_stat);//获取文件状态
if(-1 == stat_ret)
{
perror("fail to fstat");
close(fd_src);
return -1;
}
int fd_cli = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == fd_cli)
{
perror("fail to socket");
return -1;
}
//发送文件大小
sendto(fd_cli,&(file_stat.st_size),sizeof(&file_stat.st_size),0,(struct sockaddr *)&ser,sizeof(ser));
printf("%ld\n",file_stat.st_size);
char R_file_buf[1024] = {
0};
ssize_t nret = 0;
while (1)
{
nret = read(fd_src,R_file_buf,sizeof(R_file_buf));
printf("%ld\n",nret);
if(nret <= 0)
{
break;
}
sendto(fd_cli,R_file_buf,nret,0,(struct sockaddr *)&ser,sizeof(ser));
}
close(fd_src);
return 0;
}
サーバ
#include "head.h"
int main(int argc, char const *argv[])
{
struct sockaddr_in cli;
struct sockaddr_in ser = {
.sin_family = AF_INET,
.sin_port = htons(50000),
.sin_addr.s_addr = inet_addr("192.168.1.117"),
};
int fd_dst = open("./dst.jpg",O_RDWR | O_CREAT | O_TRUNC,0664);//打开文件
if(-1 == fd_dst)
{
perror("fail to open");
return -1;
}
int fd_ser = socket(AF_INET,SOCK_DGRAM,0);
if(-1 == fd_ser)
{
perror("fail to socket");
close(fd_dst);
return -1;
}
int ret_bint = bind(fd_ser,(struct sockaddr *)&ser,sizeof(ser));
if(-1 == ret_bint)
{
perror("fail to bind");
close(fd_dst);
return -1;
}
//接收文件大小
char file_size[64] = {
0};
socklen_t clilen = sizeof(cli);
ssize_t ret_re = recvfrom(fd_ser,file_size,sizeof(file_size),0,(struct sockaddr *)&cli,&clilen);
ssize_t size = 0;
char tmpbuff[1024] = {
0};
while (1)
{
ssize_t nret = recvfrom(fd_ser,tmpbuff,sizeof(tmpbuff),0,(struct sockaddr *)&cli,&clilen);
write(fd_dst,tmpbuff,nret);
if(nret != 1024)
{
break;
}
}
close(fd_ser);
}
結果グラフ
5 UDP プログラミングの一般的なフレームワーク
クライアント | サーバ |
---|---|
1ソケット | 1ソケット |
2銭湯 | 2 バインド |
3 受信元 | 3 受信元 |
4 閉じる | 4 2 送信 |
5閉じる |