基于`Caché/IRIS`实现`Socket`解决方案

Posted yaoxin521123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于`Caché/IRIS`实现`Socket`解决方案相关的知识,希望对你有一定的参考价值。

文章目录

基于 Caché/IRIS实现 Socket解决方案

下文通过一些示例和代码介绍一下在Caché/IRIS中如何使用Scoket功能。

简介

什么是Socket

所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。

Socket有两种类型

Socket的类型通常分为两种:流式SocketStream Socket)和数据报式SocketDatagram Socket)。

  1. 流式Socket:流式Socket提供面向连接的、可靠的数据传输服务,如TCP协议。它们使用基于字节流的数据传输方式,保证数据的可靠性和有序性。流式Socket适用于需要高可靠性、高效性的应用场景,如文件传输、邮件传输、网页访问等。
  2. 数据报式Socket:数据报式Socket提供无连接、不可靠的数据传输服务,如UDP协议。它们使用基于数据包的数据传输方式,不保证数据的可靠性和有序性。数据报式Socket适用于需要快速、实时的应用场景,如音视频传输、实时游戏等。

使用场景

  1. 创建客户端/服务器应用程序:使用Socket可以轻松地创建客户端/服务器应用程序,实现不同计算机之间的通信。
  2. 实现远程过程调用(RPC):Socket可以用于实现远程过程调用,使得不同计算机之间的应用程序能够调用和执行彼此的函数和过程。
  3. 实现多人在线游戏:多人在线游戏通常需要使用Socket来实现玩家之间的通信和协作。
  4. 实现实时聊天应用程序:使用Socket可以轻松地实现实时聊天应用程序,使得用户能够在不同计算机之间进行实时的文本或语音通信。
  5. 实现文件传输:使用Socket可以实现在不同计算机之间传输文件或数据,如FTP协议。

注:文章后面示例会展示对应功能代码。

SocketWeb Socket的区别

大多数人容易把SocketWeb Socket的搞混,其实两者是两个不同的东西。举个不恰当的例子可类比为Javajavascript

  1. Socket是一种底层的网络通信技术,可以用于实现多种网络应用,如HTTPFTPSMTP等。它是基于TCPUDP协议的,提供了可靠的、面向连接的数据传输服务。而Web Socket是一种专门为Web应用设计的网络通信技术,基于HTTP协议,使用了类似HTTP的握手过程,在客户端和服务器之间建立长连接,实现实时、双向的数据传输。
  2. Socket是需要程序员自行实现的底层通信技术,需要处理很多细节,比如连接、断开、发送和接收数据等。而Web Socket则是浏览器原生支持的技术,可以直接在浏览器中使用,无需编写复杂的代码。
  3. Socket通常需要借助第三方库或框架来使用,比如Java中的Socket类、Python中的Socket模块等。而Web Socket可以直接使用浏览器的WebSocket API,在浏览器中实现网络通信。
  4. Socket可以在任何应用程序中使用,比如桌面应用程序、移动应用程序、服务器端应用程序等。而Web Socket则主要用于Web应用程序中,比如在线聊天、实时游戏等。
  5. SocketOSI七层模型中的会话层、Web SocketOSI七层模型中的应用层。

工作流程

要通过Socket进行通信,至少需要一对套接字,其中一个运行于客户端,我们称之为 Client Socket,另一个运行于服务器端,我们称之为 Server Socket

简化流程为:服务器监听->客户端请求->连接确认。

  • 服务器监听:申请一个Socket一个端口上开启监听,等待连接。
  • 客户端请求:申请一个Socket连接服务器(指明IP地址和端口号)。
  • 连接确认:服务器端接到连接请求后,与客户端建立连接并进行通讯。

通过OSI模型图了解Socket所在的位置。

示例


1. 利用Socket实现简单客户端、服务器通信。

客户端方法:

ClassMethod ClientSocket()

	#; 客户端通信、客户端需要设置服务器IP与端口号
	#dim clientSocket As %IO.Socket = ##class(%IO.Socket).%New()	
	s host = "127.0.0.1" 
	s port = 7788
	s clientSocket.TranslationTable="UTF8"
	d clientSocket.Open(host, port, .sc)	//注释1
	w "连接服务器成功!",!
	
	if (clientSocket.IsOpenGet()) 	//注释2
		w "端口已经打开",!
		
		s content = "客户端发送内容:"
		d clientSocket.WriteLine(content)	//注释3

		s content = "我是yx over!"
		d clientSocket.WriteLine(content)

		d clientSocket.Flush(.sc)
		w "收到服务器的消息:"_clientSocket.ReadAny(),!
		d clientSocket.Close()	//注释4

	
	
	q $$$OK

  • 注释1:声明客户端%IO.Socket对象,设置IPPort、编码,调用Open方法打开连接。
  • 注释2:判断服务端端口已经打开。
  • 注释3:端口打开后发送消息给服务端。
  • 注释4:发送后关闭连接。

服务端方法:

ClassMethod SeverSocket()

	#; 服务端通信、服务端需要打开端口,等待客户端通信
	#dim severSocket As %IO.ServerSocket = ##class(%IO.ServerSocket).%New()
	  
	s port = 7788
	s severSocket.TranslationTable="UTF8"
	s severSocket.ConnectionQueueSize = 2
	s severSocket.LineTerminator = $c(13,10)
	d severSocket.Open(port, 10, .sc)	//注释1
	q:($$$ISERR(sc)) "Open:" _ $System.Status.GetOneErrorText(sc)	
	
	d severSocket.Listen(10, .sc)	//注释2
	q:($$$ISERR(sc)) "Listen:" _ $System.Status.GetOneErrorText(sc)
	
	if (severSocket.IsOpen &&severSocket.IsConnected) 	//注释3
		
		s ret =  severSocket.Read(1024, -1, .sc)	//注释4
		w "接收客户端的消息:" _ ret,!
		d severSocket.Write("服务器返回消息:欢迎来到yx的世界")
		d severSocket.Flush(.sc)
		d severSocket.Disconnect()	//注释5
		d severSocket.Close()	//注释6
		
	
	
	q $$$OK

  • 注释1:声明客户端%IO.ServerSocket对象,设置监听Port、编码、连接排队数量、终止符,调用Open方法打开连接。
  • 注释2:Open方法只是打开连接,还需要调用Listen方法监听连接。
  • 注释3:判断端口是否打开、是否有连接。
  • 注释4:读取客户端发送信息。
  • 注释5:断开连接,并保持监听。
  • 注释6:断开监听与连接。

调用方法:

  • 使用Caché作为服务端,IRIS作为客户端进行通信。

  • 图中命名空间也不同,可成功接收客户端服务端消息。


2. 利用Socket实现客户端服务端文件传输。

客户端方法:

ClassMethod ClientSocketFile(path)

	#; 客户端通信、客户端需要设置服务器IP与端口号
	#dim clientSocket As %IO.Socket = ##class(%IO.Socket).%New()
	s host = "127.0.0.1" 
	s port = 7788
	s clientSocket.TranslationTable = "UTF8"
	
	d clientSocket.Open(host, port, .sc)
	w "连接服务器成功!",!
	
	if (clientSocket.IsOpenGet()) 
		w "端口已经打开",!
		
		s file = ##class(%Stream.FileBinary).%New()  //注释1
    	s file.Filename = $g(path)   
		while ('file.AtEnd)         
			d clientSocket.Write(file.Read())	//注释2
		

		d clientSocket.Flush(.sc)
		d clientSocket.Close()

	
	
	q $$$OK

  • 注释1:获取文件流。
  • 注释2:将文件流传送给服务端。

服务端方法:

ClassMethod SeverSocketFile(path)

	#; 服务端通信、服务端需要打开端口,等待客户端通信
	#dim severSocket As %IO.ServerSocket = ##class(%IO.ServerSocket).%New()
	s port = 7788
	s severSocket.TranslationTable="UTF8"
	s severSocket.ConnectionQueueSize = 2
	
	d severSocket.Open(port, 10, .sc)
	q:($$$ISERR(sc)) "Open:" _ $System.Status.GetOneErrorText(sc)
	
	d severSocket.Listen(10, .sc)
	q:($$$ISERR(sc)) "Listen:" _ $System.Status.GetOneErrorText(sc)
	
	if (severSocket.IsOpen &&severSocket.IsConnected) 
		
		#dim fileStream as %Stream.FileBinary = ##class(%Stream.FileBinary).%New()  
		s sc = fileStream.LinkToFile(path)	 //注释1
		while ('severSocket.AtEnd)         
			d fileStream.Write(severSocket.Read())	 //注释2
		
		d fileStream.%Save()
		
		d severSocket.Flush(.sc)
		d severSocket.Disconnect()
		d severSocket.Close()
		
	

	q $$$OK

  • 注释1:设置获取文件名称。
  • 注释2:获取客户端文件流。

调用方法:

  • 途中可观察,客户端把socketfile.pdf文件传输给服务端并生成socketfile2.pdf文件。


3. 利用Socket实现实时聊天应用程序

客户端方法:

/// w ##class(M.Socket).ClientSocketChat()
ClassMethod ClientSocketChat()

	#; 客户端通信、客户端需要设置服务器IP与端口号
	#dim clientSocket As %IO.Socket = ##class(%IO.Socket).%New()
	s host = "127.0.0.1" 
	s port = 7788
	s clientSocket.TranslationTable = "UTF8"
	
	d clientSocket.Open(host, port, .sc)
	w "连接服务器成功!",!
	
	d clientSocket.WriteLine("来自客户端的消息")
	
	while (1)  //注释1

		s data = clientSocket.ReadLine()	//注释2
		if (data '= "" )	//注释3
			w !,"接收服务端的消息:" _ data,!
			w "请输入消息:"
			r msg	//注释4
			d clientSocket.WriteLine(msg)	
			q:(msg = "q")	//注释5
		
		
	
	
	q $$$OK

  • 注释1:使用while循环持续监听服务端消息。
  • 注释2:获取服务端消息。
  • 注释3:当消息不为空时,获取消息并输出。
  • 注释4:读取消息后,用Read命令进行回复消息。
  • 注释5:输入q字符结束通话。

服务端方法:

/// w ##class(M.Socket).SeverSocketChat()
ClassMethod SeverSocketChat()

	#; 服务端通信、服务端需要打开端口,等待客户端通信
	#dim severSocket As %IO.ServerSocket = ##class(%IO.ServerSocket).%New()
	s port = 7788
	s severSocket.TranslationTable="UTF8"
	s severSocket.ConnectionQueueSize = 2
	
	d severSocket.Open(port, 10, .sc)
	q:($$$ISERR(sc)) "Open:" _ $System.Status.GetOneErrorText(sc)
	
	d severSocket.Listen(10, .sc)
	q:($$$ISERR(sc)) "Listen:" _ $System.Status.GetOneErrorText(sc)
	
	while (1) 
		s data =  severSocket.ReadLine()
		if (data '= "") 
			w !,"接收客户端的消息:" _ data,!
			w "请输入消息:"
			r msg
			d severSocket.WriteLine(msg)
			q:(msg = "q")
		
	

	q $$$OK

  • 服务端原理与客户端同理。

调用方法:


4. 利用Socket实现RPC远程程序调用

说明:RPC框架与具体的协议无关。RPC 可基于 HTTPTCP 协议,Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC

客户端方法:

ClassMethod ClientSocketRPC()

	#; 客户端通信、客户端需要设置服务器IP与端口号
	#dim clientSocket As %IO.Socket = ##class(%IO.Socket).%New()
	s host = "127.0.0.1" 
	s port = 7788
	s clientSocket.TranslationTable = "UTF8"
	
	d clientSocket.Open(host, port, .sc)
	
	s obj = 	//注释1
	s obj.class = "M.Socket"
	s obj.method = "Add"
	s obj.params = [1,2,3,4]
	
	d clientSocket.WriteLine(obj.%ToJSON())	//注释2
	
	while (1) 

		s data = clientSocket.ReadLine() 
		if (data '= "" )	//注释3
			ret data
		
		
	
	
	q $$$OK

  • 注释1:将调用服务端的类、方法、参数组装成JSON数据。
  • 注释2:将数据发送给远端。
  • 注释3:返回处理之后的结果。

服务端方法:

ClassMethod SeverSocketRPC()

	#; 服务端通信、服务端需要打开端口,等待客户端通信
	#dim severSocket As %IO.ServerSocket = ##class(%IO.ServerSocket).%New()
	s port = 7788
	s severSocket.TranslationTable="UTF8"
	s severSocket.ConnectionQueueSize = 2
	
	d severSocket.Open(port, 10, .sc)
	q:($$$ISERR(sc)) "Open:" _ $System.Status.GetOneErrorText(sc)
	
	d severSocket.Listen(10, .sc)
	q:($$$ISERR(sc)) "Listen:" _ $System.Status.GetOneErrorText(sc)
	
	while (1) 
		s data =  severSocket.ReadLine()
		if (data '= "") 
			s obj = .%FromJSON(data)	//注释1
			s arg = obj.params.%Size()
			for i = 1 : 1 : arg
				s arg(i) = obj.params.%Get(i - 1)
			
			s ret = $classmethod(obj.class, obj.method, arg...)	//注释2
			d severSocket.WriteLine(ret)	//注释3
			q
		
	

	q $$$OK

  • 注释1:获取客户端发送过来的数据,将数据转为JSON对象。
  • 注释2:将解析的类、方法、参数利用$classmethod进行反射调用。
  • 注释3:将结果返回给客户端。

服务器PRC调用Add方法:

  • 该方法应保存在远端。
ClassMethod Add(a, b, c, d)

	q ((a + b) * c) - d

调用方法:

  • 参数为 1,2,3,4,结果为:
(1 + 2) * 3 -4 = 5

总结

Socket非常类似于打电话:

  1. 首先要知道对方的电话号码,并且双方都有电话。电话相当于Socket,电话号码相当于IP:Port
  2. 然后拨号呼叫,相当于发送连接请求,如果对方手机空闲,没有占线,拿起电话就可以互相通话了,通话相当于传输数据或交换数据。
  3. 通话结束后,一方挂起电话机相当于关闭Socket,关闭连接。

以上是个人对基于Socket的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。

第八章 Caché 设计模式 外观模式

第八章 Caché 设计模式 外观模式

定义

为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

优点

  • 减少系统的相互依赖,所以的依赖都是对外观类的依赖,与子系统无关。

  • 对用户隐藏了子系统的具体实现,减少用户对子系统的耦合,这样即使具体的子系统发生了变化,用户也感知不到。

  • 加强了安全性,子系统中的方法如果不在外观类中开通,就无法访问到子系统中的方法。

缺点

  • 不符合开放封闭原则,如果业务出现变更,则可能要直接修改外观类。

使用场景

  • 构建一个有层次结构的子系统时,使用外观模式定义子系统中每层的入口点。

  • 如果子系统之间是相互依赖的,则可以让其通过外观接口进行通信,减少子系统之间的依赖关系。

  • 子系统往往会因为不断的重构演化而变得越来越复杂,大多数的模式使用也会产生很多很小的类,这给外部调用他们的用户程序带来了使用上的困难。

  • 我们可以使用外观类提供一个简单的接口,对外隐藏子系统的具体实现并隔离变化。

  • 当维护一个遗留的大型系统时,可能这个系统已经非常难以维护和拓展;但因为它含有重要的功能。所以新的需要必须依赖于它,这时可以使用外观类

  • 为设计粗糙或者复杂的遗留代码提供一个简单的接口,让新系统和外观类交互,而外观类负责与遗留的代码进行交互。

何时使用外观模式

  • 首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典的三层架构,就需要考虑在数据访问层和业务逻辑层,业务逻辑层和表示层的层与层之间建立外观facade,这样可以为复杂的子系统提供一个简单的接口,使用耦合大大降低。

  • 其次,在开发阶段,子系统往往因为为不断的重构演化而变得越来越复杂,大多数模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用他们的用户程序带来了使用上的困难,增加外观模式Facade可以提供一个简单的接口,减少他们之间的依赖。

  • 第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含很多非常重要的功能,新的需求开发必须要依赖它,此时用外观模式也是非常合适的。

  • 可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

结构图


完整示例

子系统类

 
   
   
 
  1. Class PHA.YX.Design.Facade.SubSystemOne Extends %RegisteredObject

  2. {


  3. Method MethodOne()

  4. {

  5. w "子系统方法一",!

  6. }


  7. }

 
   
   
 
  1. Class PHA.YX.Design.Facade.SubSystemTwo Extends %RegisteredObject

  2. {


  3. Method MethodTwo()

  4. {

  5. w "子系统方法二",!

  6. }


  7. }

 
   
   
 
  1. Class PHA.YX.Design.Facade.SubSystemThree Extends %RegisteredObject

  2. {


  3. Method MethodThree()

  4. {

  5. w "子系统方法三",!

  6. }


  7. }

 
   
   
 
  1. Class PHA.YX.Design.Facade.SubSystemFour Extends %RegisteredObject

  2. {


  3. Method MethodFour()

  4. {

  5. w "子系统方法四",!

  6. }


  7. }

外观类

 
   
   
 
  1. Class PHA.YX.Design.Facade.Facade Extends %RegisteredObject

  2. {


  3. Property mSubSystemOne As SubSystemOne;


  4. Property mSubSystemTwo As SubSystemTwo;


  5. Property mSubSystemThree As SubSystemThree;


  6. Property mSubSystemFour As SubSystemFour;


  7. Method %OnNew() As %Status [ Private, ServerOnly = 1 ]

  8. {

  9. s ..mSubSystemOne = ##class(SubSystemOne).%New()

  10. s ..mSubSystemTwo = ##class(SubSystemTwo).%New()

  11. s ..mSubSystemThree = ##class(SubSystemThree).%New()

  12. s ..mSubSystemFour = ##class(SubSystemFour).%New()

  13. Quit $$$OK

  14. }


  15. Method MethodA()

  16. {

  17. w "MethodA()",!

  18. d ..mSubSystemOne.MethodOne()

  19. d ..mSubSystemTwo.MethodTwo()

  20. d ..mSubSystemThree.MethodThree()

  21. }


  22. Method MethodB()

  23. {

  24. w "MethodB()",!

  25. d ..mSubSystemTwo.MethodTwo()

  26. d ..mSubSystemThree.MethodThree()

  27. d ..mSubSystemFour.MethodFour()

  28. }


  29. }

调用

 
   
   
 
  1. /// d ##class(PHA.YX.Design.Program).Facade()

  2. ClassMethod Facade()

  3. {

  4. #dim mFacade as PHA.YX.Design.Facade.Facade

  5. s mFacade = ##class(PHA.YX.Design.Facade.Facade).%New()

  6. d mFacade.MethodA()

  7. w !

  8. d mFacade.MethodB()

  9. }

 
   
   
 
  1. DHC-APP>d ##class(PHA.YX.Design.Program).Facade()

  2. MethodA()

  3. 子系统方法一

  4. 子系统方法二

  5. 子系统方法三


  6. MethodB()

  7. 子系统方法二

  8. 子系统方法三

  9. 子系统方法四

思考

有一些武林绝学,比如,内功(九阳神功,乾坤大挪移),招式(太极拳,七伤拳,圣火令)。外观随意组合这些招数应对不同的敌人。感兴趣的同学实现后可以发我一起参考下。


以上是关于基于`Caché/IRIS`实现`Socket`解决方案的主要内容,如果未能解决你的问题,请参考以下文章

第八章 Caché 设计模式 外观模式

第六章 Caché 设计模式 原型模式

Caché高级开发—设计模式 培训总结

第十二章 Caché 设计模式 状态模式

第二章 Caché 算法与数据结构 数组原理

第十七章 Caché 设计模式 单例模式