FreeRDP的连接过程分析

Posted 道亦无名

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FreeRDP的连接过程分析相关的知识,希望对你有一定的参考价值。

代码如下:

/** Creates a new connection based on the settings found in the "instance" parameter
 *  It will use the callbacks registered on the structure to process the pre/post connect operations
 *  that the caller requires.
 *  @see struct rdp_freerdp in freerdp.h
 *
 *  @param instance - pointer to a rdp_freerdp structure that contains base information to establish
 * the connection. On return, this function will be initialized with the new connection's settings.
 *
 *  @return TRUE if successful. FALSE otherwise.
 *
 */
BOOL freerdp_connect(freerdp* instance)
{
	UINT status2 = CHANNEL_RC_OK;
	rdpRdp* rdp;
	BOOL status = TRUE;
	rdpSettings* settings;
	ConnectionResultEventArgs e;

	if (!instance)
		return FALSE;

	WINPR_ASSERT(instance->context);

	/* We always set the return code to 0 before we start the connect sequence*/
	instance->ConnectionCallbackState = CLIENT_STATE_INITIAL;
	freerdp_set_last_error_log(instance->context, FREERDP_ERROR_SUCCESS);
	clearChannelError(instance->context);
	ResetEvent(instance->context->abortEvent);

	rdp = instance->context->rdp;
	WINPR_ASSERT(rdp);

	settings = instance->settings;
	WINPR_ASSERT(settings);

	freerdp_channels_register_instance(instance->context->channels, instance);

	if (!freerdp_settings_set_default_order_support(settings))
		return FALSE;

	IFCALLRET(instance->PreConnect, status, instance);
	instance->ConnectionCallbackState = CLIENT_STATE_PRECONNECT_PASSED;

	if (status)
		status2 = freerdp_channels_pre_connect(instance->context->channels, instance);
/*如果键盘是KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002类型的, 需要进一步指定
keyboardtype keyboardsubtype和keybooardfunctionkey */
	if (settings->KeyboardLayout == KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002)
	{
		settings->KeyboardType = 7;
		settings->KeyboardSubType = 2;
		settings->KeyboardFunctionKey = 12;
	}

	if (!status || (status2 != CHANNEL_RC_OK))
	{
		freerdp_set_last_error_if_not(instance->context, FREERDP_ERROR_PRE_CONNECT_FAILED);

		WLog_ERR(TAG, "freerdp_pre_connect failed");
		goto freerdp_connect_finally;
	}

	status = rdp_client_connect(rdp);

	/* Pointers might have changed inbetween */
	if (rdp && rdp->settings)
	{
		/* --authonly tests the connection without a UI */
		if (rdp->settings->AuthenticationOnly)
		{
			WLog_ERR(TAG, "Authentication only, exit status %" PRId32 "", status);
			goto freerdp_connect_finally;
		}

		if (rdp->settings->DumpRemoteFx)
		{
			rdp->update->pcap_rfx = pcap_open(rdp->settings->DumpRemoteFxFile, TRUE);

			if (rdp->update->pcap_rfx)
				rdp->update->dump_rfx = TRUE;
		}
	}

	if (status)
	{
		pointer_cache_register_callbacks(instance->context->update);
		IFCALLRET(instance->PostConnect, status, instance);
		instance->ConnectionCallbackState = CLIENT_STATE_POSTCONNECT_PASSED;

		if (status)
			status2 = freerdp_channels_post_connect(instance->context->channels, instance);
	}
	else
	{
		if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_CONNECT_TRANSPORT_FAILED)
			status = freerdp_reconnect(instance);
		else
			goto freerdp_connect_finally;
	}

	if (!status || (status2 != CHANNEL_RC_OK) || !update_post_connect(instance->update))
	{
		WLog_ERR(TAG, "freerdp_post_connect failed");

		freerdp_set_last_error_if_not(instance->context, FREERDP_ERROR_POST_CONNECT_FAILED);

		status = FALSE;
		goto freerdp_connect_finally;
	}

	if (instance->settings->PlayRemoteFx)
	{
		wStream* s;
		rdpUpdate* update;
		pcap_record record;
		update = instance->update;
		update->pcap_rfx = pcap_open(settings->PlayRemoteFxFile, FALSE);
		status = FALSE;

		if (!update->pcap_rfx)
			goto freerdp_connect_finally;
		else
			update->play_rfx = TRUE;

		status = TRUE;

		while (pcap_has_next_record(update->pcap_rfx) && status)
		{
			pcap_get_next_record_header(update->pcap_rfx, &record);

			s = transport_take_from_pool(rdp->transport, record.length);
			if (!s)
				break;

			record.data = Stream_Buffer(s);
			pcap_get_next_record_content(update->pcap_rfx, &record);
			Stream_SetLength(s, record.length);
			Stream_SetPosition(s, 0);

			if (!update_begin_paint(update))
				status = FALSE;
			else
			{
				if (update_recv_surfcmds(update, s) < 0)
					status = FALSE;

				if (!update_end_paint(update))
					status = FALSE;
			}

			Stream_Release(s);
		}

		pcap_close(update->pcap_rfx);
		update->pcap_rfx = NULL;
		goto freerdp_connect_finally;
	}

	if (rdp->errorInfo == ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES)
		freerdp_set_last_error_log(instance->context, FREERDP_ERROR_INSUFFICIENT_PRIVILEGES);

	transport_set_connected_event(rdp->transport);

freerdp_connect_finally:
	EventArgsInit(&e, "freerdp");
	e.result = status ? 0 : -1;
	PubSub_OnConnectionResult(instance->context->pubSub, instance->context, &e);

	if (!status)
		freerdp_disconnect(instance);

	return status;
}

主要功能:

主要进行链接服务器前的客户端接口绑定,channels的连接前处理, 链接, 连接后的客户端接口处理, channels连接后处理,链接失败处理逻辑

重置一些必要参数:setting,rdp,status,wStream,Event,Error等.

代码分析:

IFCALLRET(instance->PreConnect, status, instance);
调用连接前函数,PreConnect , 通过跟踪代码发现调用
下面的函数

/**
 * 获取本机的显示器分辨率信息,加载channels信息,设置键盘信息
 * @param instance
 * @return
 */
static BOOL wl_pre_connect(freerdp* instance)
{
	rdpSettings* settings;
	wlfContext* context;
	const UwacOutput* output;
	UwacSize resolution;

	if (!instance)
		return FALSE;

	context = (wlfContext*)instance->context;
	settings = instance->settings;

	if (!context || !settings)
		return FALSE;

	settings->OsMajorType = OSMAJORTYPE_UNIX;
	settings->OsMinorType = OSMINORTYPE_NATIVE_WAYLAND;
	PubSub_SubscribeChannelConnected(instance->context->pubSub, wlf_OnChannelConnectedEventHandler);
	PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
	                                    wlf_OnChannelDisconnectedEventHandler);

	if (settings->Fullscreen)
	{
		// Use the resolution of the first display output
		output = UwacDisplayGetOutput(context->display, 0);

		if ((output != NULL) && (UwacOutputGetResolution(output, &resolution) == UWAC_SUCCESS))
		{
			settings->DesktopWidth = (UINT32)resolution.width;
			settings->DesktopHeight = (UINT32)resolution.height;
		}
		else
		{
			WLog_WARN(TAG, "Failed to get output resolution! Check your display settings");
		}
	}

	if (!freerdp_client_load_addins(instance->context->channels, instance->settings))
		return FALSE;

	return TRUE;
}

可以看到将分辨率信息赋值给settings。
加载channels信息, 通过freerdp_client_load_addins进行参数传递。

链接RDP服务器函数接口, 包括协议选择, socket建立等

为记录remoteFX的dump信息创建文件

目前就分析到这个地方,后面继续进行分析。待续~~~~。

以上是关于FreeRDP的连接过程分析的主要内容,如果未能解决你的问题,请参考以下文章

linux下使用 FreeRDP 连接 Windows 远程桌面(转)

FreeRDP 是不是支持动态屏幕大小的显示更新通道

FreeRDP简介

云桌面 --- FreeRDP使用说明

编译freerdp需要的一些依赖库

编译freerdp需要的一些依赖库