网络编程16——UDP实现的并发服务器和客户端

TCP、UDP通信的各自优缺点

TCP: 面向连接的, 可靠,数据报传输。对于不稳定的网络层,采取完全弥补的通信方式,即丢包重传
优点:稳定,数据流量稳定、速度稳定、顺序稳定
缺点:传输速度慢,速率低,开销大
使用场景:数据的完整性要求较高,不追求效率,如大数据传输,文件传输

UDP:无连接的,不可靠的,数据报传输,对于不稳定的网络层,采取完全不弥补的通信方式。默认还原网络状况
优点:传输速度快,速率高,开销小
缺点:不稳定,数据流量、速度、顺序都不稳定
使用场景:对时效性要求较高,对稳定性其次,如游戏、视频会议、视频电话

UDP实现的CS模型:

recv()/send()函数只能用于TCP通信,替代read、write
server:不用三次握手去建立连接了,connect()被舍弃,accept()被舍弃
1,lfd = socket(AF_INET, SOCK_DGRAM,0); //第二个参数SOCK_DGRAM表示报式协议,若STREAM则表示流式协议
2,bind();绑定地址结构
3,listen();同时设置三次握手的数量,所以listen可有可无,用不上
4,while(1){
read(cfd, buf,) //read函数被替换,用recvfrom()
recvfrom();
toupper();
write()也被替换,用sendto()
}
5,close();

client:
1,connfd = socket(AF_INET, SOCK_DGRAM,0); //报式协议
2,sendto(connfd,“服务器的地址结构”, 地址结构长度); //代替conncet();
3,recvfrom();//想成原来的read()就行
4,写到屏幕
5,close();

recvfrom()函数和sendto()函数

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
看参数,所以recvfrom函数不单替代了read()函数的功能呢,还涵盖了accept()返回地址结构的作用
sockfd:lfd
buf:缓冲区地址
len:缓冲区大小
flags:默认0
src_addr:(struct sockaddr*)&addr传出参数,传出对端地址结构
addrlen:传入传出参数
**返回值:**成功接收数据字节数;失败-1 errno;对端关闭0

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字
buf:存储数据的缓冲区地址
len:缓冲区大小
flags:默认0
src_addr:传入参数,目标地址结构(要将数据发送给哪个服务器
addrlen:地址结构长度
**返回值:**成功写出数据字节数;失败-1 errno;对端关闭0

UDP实现的并发服务器和客户端

server端👇:

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<ctype.h>

#define SERV_PORT 8000

int main(void)
{
	int sockfd;
	struct sockaddr_in serv_addr, clie_addr;
	socklen_t clie_addr_len;
	int i, n;
	char buf[BUFSIZ];
	char str[INET_ADDRSTRLEN];//在网路地址转换时用inet_ntop()
//①
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//②
	bzero(&serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(SERV_PORT);
	bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	printf("Accepting connection...\n");
//舍去listen函数,不用accept,直接就recvfrom和sendto
	while(1)
	{
		clie_addr_len = sizeof(clie_addr);
		n = recvfrom(sockfd, buf,BUFSIZ,0,(struct sockaddr*)&clie_addr, &clie_addr_len);//从客户端中去读取
		if(n == -1)
			perror("recvfrom error");
		printf("received from %s at PORT %d \n", inet_ntop(AF_INET, &clie_addr.sin_addr, sizeof(str)), ntohs(clie_addr.sin_port));
//读
		for(i = 0; i<n; i++)
			buf[i] = toupper(buf[i]);
//操作
		n = sendto(sockfd, buf, n, 0, (struct sockaddr*)&clie_addr, sizeof(clie_addr));
		if(n == -1)
			perror("sendto error");	
	}
	close(sockfd);
	return 0;
}

client端👇:

#include<stdio.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

#define SERV_PORT 8000

int main(int argc, char *argv[])
{
	int sockfd, n;
	struct sockaddr_in servaddr;//只要去连接的服务器的地址就行了
	char buf[BUFSIZ];
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servadd.sin_family = AF_INET;
	inet_pton(AF_INET, "127.0.0.1",&serv_addr.sin_addr);	//服务器的ip地址?传网路地址
	servaddr.sin_port = htons(SERV_PORT);
	bind(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr));
	//客户端就先写sendto,再读回来
	while(fgets(buf,BUFSIZ,stdin) != NULL)
	{
		n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&servaddr, sizeof(servaddr));
		if(n == -1)
			perror("sendto error");
		n = recvfrom(sockfd, buf, BUFSIZ,0,NULL,0);//NULL表示不关心对端信息
		if(n ==-1)
			perror("recvfrom error");
		write(STDOUT_FILENO, buf, n);	
	}
	close(sockfd);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
bind()上两个函数几乎是一样的,就是对serv_addr初始化的时候对服务器的ip地址转换不同,服务器就是htonl(INADDR_ANY); 对客户端需要去将已知的ip地址比如"127.0.0.1"转换成网络传输的用到inet_pton()函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值