36. UDP套接字之有连接通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Function_Dou/article/details/89949663

上一节最后发现服务端不存在客服端并不知道, 会被一直阻塞.


错误回顾

如果你忘了启动服务端, 你会发现客服端依旧被阻塞了, 并且没有报错. 而且发送数据即使没有收到应答的数据也没有报错, 程序仍然处于阻塞状态.

在这里插入图片描述


错误解析

其实在客服端与服务端主机发送UDP数据报之前, 需要一次arp请求和应答交换(我是在一个主机上实验, 所以无法抓取到).

我们在通过抓包可以看到,服务端会响应一个port unreachable的ICMP应答, 不过这个ICMP并不会返回给客户进程, 这个错误是sendto导致的, 而sendto将数据发送出去之后就返回成功了, 而ICMP是在之后才发出来, 所以这个错误也称ICMP异步错误.

在这里插入图片描述

这个错误只会在已连接时, 才会返回给客服进程. 说了这么多次已连接, 那么接下来我们就来看一下怎么才能让UDP已连接.


UDP已连接

客服端通过调用connect完成UDP已连接的状态, 但不同于TCP, UDP调用connect不会出现三次握手的现象, 只是用来记录对端的IP地址和端口号, 然后返回给调用进程.

已连接的UDP相对于未连接的UDP发生部分变化 :

  • 不再给recvfrom输出上面绑定IP地址和端口号, 也不用让sendto获取对端的IP地址和端口号信息; 也就是可以直接调用writeread函数.
  • 可以收到ICMP异步错误.

注意 : UDP使用connect没有三次握手并使connect并不会阻塞, 而是直接返回.

服务端的代码不用就行修改, 客服端的代码修改如下 :


完整代码 : connect_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>

#define error(s) {	\
	perror(s);	\
	exit(1);	\
}

int main(int argc, char *argv[]){
	int fdClinet;
	struct sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	inet_aton(argv[1], &serAddr.sin_addr);
	serAddr.sin_port = htons(atoi(argv[2]));
	
	fdClinet = socket(AF_INET, SOCK_DGRAM, 0);

	// 使用connect之后就可以使用 read 和 write 进行通信了
	if(connect(fdClinet, (struct sockaddr*)&serAddr, sizeof(serAddr)) == -1)
		error("connect");

	char buf[1024];
	int size;
	while(1){
		size = read(STDIN_FILENO, buf, sizeof(buf));	// 可以直接使用read函数
        if(size <= 0)
            break;
		write(fdClinet, buf, strlen(buf));
		size = read(fdClinet, buf, sizeof(buf));
        if(size <= 0)
            break;
		write(STDOUT_FILENO, buf, size);
	}
	close(fdClinet);

	return 0;
}

我们再来浮现服务端未启动的问题, 看下此时会是什么情况.

在这里插入图片描述

当客服端发送数据数据后会收到recvfrom(或者read)会ICMP异步错误, 所以直接就退出连接了.


多次调用connect

对于TCP来说客服端只能调用一次connect函数, 而UDP却不这样, UDP调用connect只是让UDP连接并没有其他含义, 所以可以多次调用connect函数.

connect调用过程 :

  • 指定新的IP地址和端口
  • 断开套接字

性能

UDP通信会经历三个步骤 :

  1. 建立连接
  2. 输出数据报
  3. 最后断开连接

而使用未连接UDP每次发送数据都会重复上述上个步骤, 即使是同一服务端通信也是如此. 使用已连接UDP建立连接后会重复步骤二直到断开连接. 也不难理解未连接UDP通信大约会耗费UDP传输的三分之一开销. 所以, 如果给同一目的地址连续的发送数据, 则使用已连接UDP更加高效.


小结

  • 已连接UDP实现
  • ICMP异步错误
  • 已连接UDP并没有三次握手. 只是绑定了IP地址和端口
  • UDP可以多次调用connect函数

猜你喜欢

转载自blog.csdn.net/Function_Dou/article/details/89949663