UDP
原理
TCP:传输控制协议 安全可靠 丢包重传 面向连接
UDP:用户数据报协议 不安全不可靠 丢包不重传 快 不面向连接
TCP通信流程:
服务器:创建流式套接字 绑定 监听 提取连接 读写 关闭
客户端:创建流式套接字 连接 读写 关闭
收发数据
read recv
write send
UDP通信流程:
服务器:创建报式套接字 绑定 读写 关闭
客户端:创建报式套接字 读写 关闭
发数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
dest_addr 目标地址信息
addrlen 结构体大小
收数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
dest_addr 对方地址信息
addrlen 结构体大小
udp因为无连接,所以每个发送消息都需要包含目的地址 sendto()中 dest_addr 目标地址信息
每个接受的消息都要提取出对方的地址sendto() 中 dest_addr 对方地址信息
udp server.cpp
#include<iostream>
#include<string>
#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <ctype.h>
using namespace std;
#define MAXLINE 128
int main(void){
struct sockaddr_in servaddr;
int sockfd;
char buf[MAXLINE];
char str[INET_ADDRSTRLEN];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("192.168.109.128");
//servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(5000);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
printf("Accepting connections ...\n");
int n;
while (1){
n=recvfrom(sockfd,buf,MAXLINE,0,(struct sockaddr *)&cliaddr, &cliaddr_len);
if(n == -1) perror("recvfrom error");
cout<<"New Connect"<<" Client ip:"<<inet_ntoa(cliaddr.sin_addr);
cout<<" Client port:"<<ntohs(cliaddr.sin_port);
cout<<" "<<buf<<endl;
n=sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
if(n == -1) perror("sendto error");
memset(buf,0,sizeof(buf));
memset(&cliaddr,0,sizeof(cliaddr_len));
}
close(sockfd);
return 0;
}
udp client.cpp
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>
#define MAXLINE 128
int main(int argc, char *argv[]){
struct sockaddr_in servaddr;
int sockfd, n;
char buf[MAXLINE];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, "192.168.109.128", &servaddr.sin_addr);
servaddr.sin_port = htons(5000);
while (fgets(buf, MAXLINE, stdin)!=NULL) {
n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(n == -1)perror("sendto error");
n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);
if (n == -1)perror("recvfrom error");
write(STDOUT_FILENO, buf, n);
}
close(sockfd);
return 0;
}
本地套接字
原理
socket API 原本是为网络通讯设计的,但后来在 socket 的框架上发展出一种 IPC 机制,就是 UNIX Domain Socket。 虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是 UNIX Domain Socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一 个进程拷贝到另一个进程。
网络套接字地址结构
struct sockaddr_in {
_kernel_sa_family_t sin_family; /* Address family */ 地址结构类型
_be16 sin_port; /* Port number */ 端口号
struct in_addr sin_addr; /* Internet address */ IP 地址
};
本地套接字地址结构:
struct sockaddr_un {
_kernel_sa_family_t sun_family; /* AF_UNIX */ 地址结构类型
char sun_path[UNIX_PATH_MAX]; /* pathname */ socket 文件名(含路径)
};
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTJZbd6H-1646450253188)(img\本地套接字.png)]
创建本地套接字->绑定->监听->提取->读写->关闭
//创建本地套接字
int socket(int domain, int type, int protocol);
domian:AF_UNIX
type:SOCK_STREAM(TCP)
protoclo:0
//绑定
int bind(int sockfd,struct sockaddr *addr,socklen_t addrlen);
sockfd:本地套接字
*addr:本地套接字结构,sockaddr_un
addrlen:sockaddr_un大小
//后面的类比
domian_s.cpp
#include<iostream>
#include<string>
#include<stdlib.h>
#include<stdio.h>
#include<stddef.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>
#include"wrap.c"
using namespace std;
int main(){
//创建unix流式套接字
int lfd=socket(AF_UNIX,SOCK_STREAM,0);
//绑定
unlink("sock.s");
struct sockaddr_un seraddr;
seraddr.sun_family=AF_UNIX;
strcpy(seraddr.sun_path,"sock.s");
bind(lfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
//监听
listen(lfd,128);
//提取
struct sockaddr_un cliaddr;
memset(&cliaddr,0,sizeof(cliaddr));
socklen_t cliaddrlen=sizeof(cliaddr);
int cfd=accept(lfd,(struct sockaddr*)&cliaddr,&cliaddrlen);
cout<<"New connect file: "<<cliaddr.sun_path<<endl;
//读写
char buf[128]={0};
while(1){
int n=Readline(cfd,buf,sizeof(buf));
cout<<"n:"<<n<<endl;
if(n<=0){
cout<<"Close file: "<<cliaddr.sun_path<<endl;
break;
}
else{
cout<<"From "<<cliaddr.sun_path<<": "<<buf<<endl;
memset(buf,0,sizeof(buf));
}
}
//关闭
close(cfd);
close(lfd);
}
domian_c.cpp
#include<iostream>
#include<string>
#include<stdlib.h>
#include<stdio.h>
#include<stddef.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>
#include"wrap.c"
using namespace std;
int main(){
//创建unix流式套接字
int cfd=socket(AF_UNIX,SOCK_STREAM,0);
//如果不绑定就是隐式绑定
unlink("sock.c");
struct sockaddr_un cliaddr;
cliaddr.sun_family=AF_UNIX;
strcpy(cliaddr.sun_path,"sock.c");
bind(cfd,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
//连接
struct sockaddr_un seraddr;
seraddr.sun_family=AF_UNIX;
strcpy(seraddr.sun_path,"sock.s");
connect(cfd,(struct sockaddr*)&seraddr,sizeof(seraddr));
//读写
char sendbuf[128]={0};
while(1){
fgets(sendbuf,sizeof(sendbuf),stdin);
write(cfd,sendbuf,strlen(sendbuf));//注意这个地方用strlen,不要用sizeof
memset(sendbuf,0,sizeof(sendbuf));
}
//关闭
close(cfd);
}
本文详细解析了UDP协议的工作原理,强调其无连接、不可靠的特点,并通过实例展示了UDP服务器与客户端的通信流程。同时,介绍了UNIX Domain Socket在本地进程间通信中的优势。
1687

被折叠的 条评论
为什么被折叠?



