【网络编程】UDP+本地套接字

本文详细解析了UDP协议的工作原理,强调其无连接、不可靠的特点,并通过实例展示了UDP服务器与客户端的通信流程。同时,介绍了UNIX Domain Socket在本地进程间通信中的优势。

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值