03 Linux环境下的UDP编程

插座第三个参数传0,表示默认协议,流式协议的典型代表就是TCP协议,报式协议的典型代表就是UTP协议。

名词解释:

BSD:(Berkeley Software Distribution 伯克利发布的软件套件),主要是网络部分。

本地套接字:

本地套接字是的的的的Unix中的中进程间通讯方式,支持进程间的通信,但不能使用在跨计算机的通信中,服务端与客户端的通信流程基本与网络套接字一致,但在形式上有两点不同:

  1. 创建插座时,传入的域参数不同。域参数应该传入本地套接字AF_UNIX,AF_LOCAL或AP_UNIX,PF_LOCAL。
    1. AF:地址族,一般传入地址时使用。
    2. PF:协议族,一般传入协议时使用。
    3. 但是宏定义的值相同,一般可以混用。
  2. 绑定的地址需要使用sockaddr_un结构体,struct sockaddr_un结构有两个参数:sun_family,sun_path.sun_family只能是AF_LOCAL或AF_UNIX,而sun_path是本地文件的路径。通常将文件放在/ tmp目录下。
    1. addr.sun_family = AF_UNIX;
    2. 的的的strcpy(addr.sun_path中中中,“server.sock”);
    3. ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));
  3. 所以还需要对文件进行验证,如果文件存在绑定就会报错。

         addr.sun_family = AF_UNIX;
         的strcpy(addr.sun_path中,“server.sock”);
         ret = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));

参考:https//www.cnblogs.com/hnrainll/archive/2011/04/24/2026433.html

Linux下的UDP套接字编程

UDP协议一般用于即时通讯的应用开发,它们对于数据的及时性要求较高,TCP的连接,验证等机制都会降低数据的传送效率。但考虑到数据的完整性问题,一些大型网络公司就会使用到udp + tcp的形式,在传输层使用UDP,在应用层使用自定义的协议,添加类似于TCP的校验方式,弥补UDP的丢包现象。

弥补UDP丢包的应用层方法,可以改变数据接收端缓冲区的大小,尽量减少缓冲区已满造成数据丢失的现象。

int n = 220 * 1024;//这是一个在应用中得出的最优值。
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

两个函数:服务端参数:

recvfrom(connfd,buf,strlen(buf),0,(struct sockaddr *)&addr,&len); //此处的地址与LEN都是传入传出参数,获取的客户端的信息,用作之后的SENDTO函数参数。

sendto(sockfd,buf,n,0,(struct sockaddr *)&addr,len); //按照指定的地址将数据发送给客户端,所以此处的LEN参数不需要取地址。若客户端不先行发送数据,服务端就无法得知其地址,无法主动发送数据。

扫描二维码关注公众号,回复: 5400487 查看本文章

UDP实现广播

TCP / IP协议栈中,只有UDP可以广播。

网管地址(网管号):主机号为全0的IP地址所有访问外网的请求都由这个网管转发,当然也可以在此网关处设置限制。

广播地址:主机号为全1的IP地址为广播地址当发出一个目的地址为广播地址的分组(封包)时,它将被分发给该网段上的所有计算机。

IP:192.168.42.255(广播)

IP:192.168.42.1(网管)

的的sockfd默认不支持广播,需要通过调用setsockopt的的函数进行修改,开放广播权限。

flag = 1;
setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));

需要注意:

  1. 127.0.0.0是本地会环地址,从网卡的发送端出去,在本地接收端返回,可以用于测试本地的C / S程序。
  2. 在服务器中,0.0.0.0指的是本机上的所有(任意)IPV4地址,如果一个主机有两个IP地址,192.168.1.1和10.1.2.1,并且该主机上的一个服务监听的地址是0.0 .0.0,那么通过两个IP地址都能够访问该服务。当然在服务器端也可以使用,还可以用INADDR_ANY(宏定义值为0)来代替。
  3. 然而对于Linux的发行版本的实际实现中,需要使用客户端需要使用任意地址,不然使用本地某个具体地址就会接收不到广播。
  4. 在同一局域网内的不同主机可以拥有两个相同的IP,即IP冲突。
  5. 在同一台计算机中的IP地址与端口号不能在程序运行时重用,在不同计算机中可以使用相同的端口号实现广播,发送方向也可以指向同一端口号。

注意:客户端地址需要使用任意,否则接收不到广播信息。这是必须的。

贴出以下实现代码:

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#include <sys/socket.h>
#include <arpa/inet.h>

#define SIZE 128

#define BROADCAST_IP "192.168.16.255" //C类IP地址
#define CLIENT_PORT 9000

#define SERVER_PORT 8000

int main(void)
{
	int ret = -1;
	int sockfd = -1;
	int flag = -1;
	char buf[SIZE];
	struct sockaddr_in addr;
	struct sockaddr_in from;
	socklen_t len = sizeof(from);
	
	//1.创建套接字
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd) {
		perror("socket");
		return 1;
	}
	printf("sockfd :%d\n", sockfd);
	//2.绑定
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERVER_PORT);
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
	if (-1 == ret) {
		perror("bind");
		return 1;
	}
	
	flag = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
	/* 构造client地址 IP + 端口,因为是服务器主动发送数据,
	 * 所以需要知道客户端的端口号,与之前基于TCP的C/S模型相比,
	 * 之前的一般由客户端主动访问服务端,而客户端的端口号是随机获取的。
	 * 而对于广播,是一种不同的业务需求,
	 * 需要服务端向范围IP端口发送数据,这时候就很有必要知道客户端程序的端口号。
	 * 所以此时服务端的端口号反倒不重要了,可以随机获取。*/
	memset(&from, 0, sizeof(from));
	from.sin_family = AF_INET;
	from.sin_port = htons(9000);
	inet_pton(AF_INET, BROADCAST_IP, &from.sin_addr.s_addr);

	//3.循环接收客户端数据
	int i = 0;
//	memset(buf, 0, SIZE);
	while (1) {
		sprintf(buf, "Drink %d glasses of water", i++);//有一端添加换行符即可。
		sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&from, sizeof(from));
		printf("--->发出数据\n");
		
		usleep(100000);
	}

	//4.关闭文件描述符
	close(sockfd);
	
	return 0;
}


client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

#include <arpa/inet.h>
#include <sys/socket.h>

#define SIZE 128
#define CLIENT_PORT 9000

int main(void)
{
	int ret = -1;
	int sockfd = -1;
	char buf[SIZE];
	struct sockaddr_in addr;
	int len = -1;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd) {
		perror("socket");
		return 1;
	}

	//初始化本地地址
	//相当于INADDR_ANY
	//区分127.0.0.1
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(CLIENT_PORT);
	ret = inet_pton(AF_INET, "0.0.0.0", &addr.sin_addr.s_addr);
	if (-1 == ret) {
		printf("inet_pton");
		return 1;
	}
	ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
	if (0 == ret) 
		printf("...bind ok...\n");
	
	memset(buf, 0, SIZE);
	/* 从网络中读取的数据,传输过来的字符串结束标记的'\0'是会被忽略的。
	 * 所以一定要注意将接受端的容器清空,这样才能够保证不读取客户端未
	 * 清空的buf后的错误数据。所以,最重要的问题就是:
	 *		1.网络传输中尾0被忽略。
	 *		2.客户端接收时没有将容器即使清零。*/
	while (1) {
		len = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, 0);
		printf("ret = %d\n", len);
		printf("--->%s\n", buf);
	}
	
	close(sockfd);
	return 0;
}

TCP UDP与

TCP:面向连接的可靠数据包传递------对当前网络环境做完全弥补

    优点:稳定

  1. 数据稳定:------丢包回传的回执机制。
  2. 速率稳定:------使用TCP方式传递的数据包在网络稳定的情况下经过的路由是固定的。
  3. 流量稳定:------滑动窗口,对每次发送的数据量进行限制。

    缺点:

  1. 效率低:回执
  2. 传输速度慢:滑动窗口

    使用场景:

  1. 大文件传输
  2. 重要文件传输

UDP:无连接的不可靠报文传递------对当前网络环境做完全不弥补,还原当前的网络状态。

    优点:效率高,速度快

    缺点:不稳定:数据,速率,流量。

    使用场景:对实时性要求较高。

猜你喜欢

转载自blog.csdn.net/Hello_MyDream/article/details/85254409