一、开篇
程序间通信的主要目的是实现多台计算机(也可以是同一台)中应用程序之间的数据共享与信息交换。在不同的计算机系统中,它们之间要通过网络之间的协议才能
实现数据共享与信息交换;在同一台计算机系统中,它们之间只需一定的通道就能实现数据共享与信息交换。在不同计算机系统和同一计算机系统的程序通信中,既
有很多相同之处,也有各自的特点。程序间通信都要靠一定的通道(pipe)来实现,其中的通道多种多样,各俱特色。
为了充分认识和掌握程序间通信及其相应的实现技术,本文对各种通信方法进行讨论。包括每种方法的原理和实现等。
首先,程序间相互独立,它的运行环境不为别的进程所改变。
SOCKET编程是一种典型的会话编程方式,类似于老师家访,敲门----有人开门----进去----交流----出门。它适用于client/server通信方式,也适用于点对点通信方式。
下面分别介绍服务端和客户端的具体任务。
这里介绍TCP的过程。
(1)服务端
服务端首先创建一个套接字,使用socket()调用;然后使用bind()将该套接字与本地IP和某一端口相关联(该端口可以是空闲的也可以是非空闲
的,具体的可以参阅笔者的《对端口截听的实现》这篇文章)。使用listen()让套接字等候进入连接,然后用accept()使套接字作好接受客户连接
的准备。当连接请求到来后,被阻塞服务进程的accept()函数生成一个新的套接字与客户套接字建立连接,并向客户返回接收信号。用read()来读入
数据,用write()来向发送进程写回一些数据,如确认信息或回显信息。
(2)客户端
客户进程也是先创建一个套接字,用socket()调用,然后客户向服务进程发出连接请求,通过调用connect()可以建立一个端的连接,连接成功后用write()向服务端发送数据,用read()读取服务端返回的数据。
这种方式隐含了在建立client/server间通信的非对称性。client/server模型工作时要求有一套为客户机和服务器所共识的惯例来保证
服务器能够被提供(或被接受),这一套惯例包括了一套协议,它必须在通信的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协
议中,每一方都有可能扮演主从角色;在非对称协议中,每一放则是从机。例如终端仿真TELNET是对称协议,HTTP是非对称协议。无论具体的协议是对称
的还是非对称的,当服务被提供时必然存在客户进程和服务进程。
这种通信方式适用于单个计算机系统,也适用于多个计算机系统。
- WSADATA wsaData;
- WORD version = MAKEWORD(2, 0);
- int ret = WSAStartup(version, &wsaData);
- if(ret != 0)
- TRACE("Initilize Error!/n");
- m_hSocket = socket(AF_INET, SOCK_STREAM,0);
- m_addr.sin_family = AF_INET;
- m_addr.sin_addr.S_un.S_addr = INADDR_ANY;
- m_addr.sin_port = htons(m_nPort);
- int ret = 0;
- int error = 0;
- ret = bind(m_hSocket, (LPSOCKADDR)&m_addr, sizeof(m_addr));
- if(ret == SOCKET_ERROR){
- TRACE("Bind Error: %d /n", (error = WSAGetLastError()));
- return ;
- }
- ret = listen(m_hSocket, 2);
- if(ret == SOCKET_ERROR){
- TRACE("Listen Error: %d /n", (error = WSAGetLastError()));
- return ;
- }
- SOCKET s = accept(m_hSocket, NULL, NULL);
- if(s == SOCKET_ERROR){
- TRACE("Accept Error: %d /n", (error = WSAGetLastError()));
- return ;
- }
- char buff[256];
- ret = recv(s, buff, 256, 0);
- if(ret == 0 || ret == SOCKET_ERROR ){
- TRACE("Recv data error: %d/n", WSAGetLastError());
- return ;
- }
- char buf[]="hello";
- send(s, buf, str.GetLength(), 0);
这种方法很原始,使用率也很少,而且占用磁盘空间。利用文件作为程序间通信的通道,程序A向一文件写入数据,程序B通过读取这个文件中的数据从而与程序A进行通信,同时这一过程也可以是反向的。这种方法使用的前提是运行程序的用户需要具有对磁盘读/写的权限。
|A.WriteFile------>fileA<------B.ReadFile(check
every <time>)
|A.ReadFile(check every
<time>)----->fileB<-----B.WriteFile
- //A
- int buff=0;
- ofstream Table;
- Table.open("c://temp.txt");
- Table<<buff<<endl;
- Table.close();
- //B
- int buff=1;
- for(;;)
- {
- ifstream Table;
- Table.open("c://temp.txt");
- Table>>buff;
- Table.close();
- if(buff==0){
- printf("Operation code equal 0");
- //...
- return;
- }
- sleep(10);
- }
这种方法适用于单个计算机系统也适用于多个计算机系统。用这种方法在多个计算机系统间进行控制和数据交换,可以将这个文件放与一计算机上,对方建立空连接,COPY此文件到本地,进行读取。当然也还有别的很多方法。
这种方法仅用于进程控制,不能进行数据交换。例如在发生非法内存访问、执行无效指令、某些按键(如CTRL+C,DEL等)等都会产生一个信号,其他程序调用有关的系统调用或用户自定义的处理过程进行处理。
信息处理的系统调用是signal,它的原型是void ( *signal( int sig, void (__cdecl *func)
( int sig [, int subcode ] )) ) ( int sig
);这个函数可以让一个进程选择很多方法中的一种去处理操作系统返回的中断讯号。
根据类似的思路,我们也可以在多个程序间利用“信号”进行程序间的通信。
这种方法的关键是产生信号。可以通过keybd_evnet()产生信号,这个函数能够起到模拟键盘输出的作用,它的函数原型是VOID
keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD
dwExtraInfo);vVk是一个虚拟键码,bScan是扫描码,对应相应的键。
这种方法适用于单个计算机系统。
管道(PIPE)是一种简单的进程间通信(IPC)机制,分为无名管道和有名管道两种。命名管道可以在同一台机器的不同进程间以及不同机器上的不同进程间
进行双向通信(使用UNC命名规范)。管道的最大好处在于:它可以象对普通文件一样进行操作,它的操作表示符HANDLE,也就是说,它可以使用
ReadFile,WriteFile函数进行与底层实现无关的读写操作,用户根本就不必了解网络间/进程间通信的具体细节。
无名管道实际上是内存中的一个临时存储区,由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出的方式管理,并严格按顺序操作,管道
不能被搜索,管道中的信息只能读一次。无名管道只是在父子进程之间或者一个进程的两个子进程之间进行通信的。它是单向的。无名管道其实是通过用给了一个指
定名字的有名管道来实现的。
有名管道的操作和无名管道类似。
管道的创建用createpipe()函数,用readfile()、writefile()对其进行操作。
- SECURITY_ATTRIBUTES a;
- HANDLE hReadPipe,hWritePipe;
- CreatePipe(&hReadPipe,&hWritePipe,&a,0);
- unsigned long lBytesRead;
- char Buff[1024];
- int ret;
- while(1)
- {
- ret=PeekNamedPipe(hReadPipe,Buff,1024,&lBytesRead,0,0);
- if(lBytesRead)
- {
- ret=ReadFile(hReadPipe,Buff,lBytesRead,&lBytesRead,0);
- if(!ret) break;
- }
- else
- {
- //lBytesRead=...;
- if(lBytesRead<=0) break;
- ret=WriteFile(hWritePipe,Buff,lBytesRead,&lBytesRead,0);
- if(!ret) break;
- }
- }
消息通信方式以消息缓冲区为中间介质,通信双方的发送和接收操作均以消息为单位。在存储器中,消息缓冲区被组织成队列,通常称为消息队列(MQ)。消息队
列是独立于生成它的进程的一段存储区,任何具有正确访问权限的进程都可以访问消息队列,它非常适用于在进程间交换短消息。
其中,每条消息有类型编号分类。消息队列在创建后将一直存在,直到使用相关的调用删除它为止。
共享储存段通信(shared
memory)允许多个进程在外部通信协议或同步/互斥机制的支持下使用同一个内存段作为中间介质进行通信,它是一种很有效的数据通信方式。
在进行通信之前,先创建一个共享内存段,然后进行映射和分离操作。同时可以根据需要改变共享内存段的存取权限以及其他的一些特征。
中间件是一类软件,它对应用程序隐含了实际网络和通信协议的细节。高级编程接口帮助开发者在不同的环境中创建应用程序,而不需要对将使用的网络和通信协议有更多的了解。
如,Microsoft的开放式数据库连接(ODBC)标准提供后端数据库系统操作的通用功能。前端应用程序写入ODBC,并利用它的功能。ODBC
隐藏了不同厂商的SQL实现的区别。Microsoft以一组Microsoft
Windows驱动程序的形式提供ODBC,以提供对Microsoft Access、Microsoft Excel、Microsoft
SQL Server、FoxPro、Btrieve,dBASE,Borland Paradox、IBM
DB2、DECRdb和Oracle等格式产生的数据的访问。ODBC是为了使Windows成为客户访问后端数据库的标准而设计的。
处理所有多协议、多厂商问题。有三种类型的中间件:远程过程调用(RPC)、会晤(conversations)和消息传递系统。它们都能很好地隐藏通信
过程,以及与它们进行操作的系统差异。
Remote Procedure Call(PRC)
一个远程过程调用是在网络上一个计算机系统对另一计算机系统的请求。RPC保持了中间件在不同网络平台和通信协议上工作的性质。基本上,一个PRC就是一
台计算机向另一台计算机发出的直接请求。它是一种请求/回答的过程,这时请求放等待一个回答,这意味着PRC通常是在面向连接界面上发生的一种实时呼叫。
RPC机制强制通信的两个应用程序必须同时处于运行状态。做远程调用时,两者必须先建立连接,而且通讯链路质量对它的效果影响很大。
它的工作方式是:当一个应用程序A需要与远程的另一个应用程序B交换信息或要求B提供协助时,A将在本地产生一个请求,通过通讯链路,同志B接收信息或提供相应的服务,B完成相关处理后将确认信息或结果返回给A。
PRC的优点是应用程序采用调用/返回方式通讯,拥有很高的潜在效率,但需要应用程序间的紧密耦合,通讯线路必须在通信期间一直保持良好的状态,而且必须进行大量的底层通讯的编程工作。
会晤(conversations)是逻辑连接的两个或多个系统之间的连续会话,同RPC不同,在分布式环境,会晤可能会重叠执行。出于这个原因,对需要
在多个地方必须完全同步地完成,修改分布式数据库的工作,会晤是非常必要和有益的。IBM的高级程序对程序通信(APPC)实现了会晤。用于实现会晤的
OSI标准也已出现。Covia
Technologies(Rosemont,Illinois)的通信集成器(CI,Communication
Integrator)是具有会晤的中间件的另一个例子、它可以在大型计算机、中型机和台式机上运行。
为了简化应用程序间的通信,使得通信具有较高的可靠性,又保证实现的简单性,中间件技术是我们的首选,IBM公司的MQSeries就是基于这种技术的产
品。MQSeries是一种独立的通信软件,应用程序只需将任务提交给该软件,就可以由该软件自动去完成信息的传递工作。
例如:
应用程序A
|
|
放入|消息
|
|
∨
MQSeries接口----------------
∧
|
|
|
|
|唤醒
读取|消息(于Q1中)
|
|
|
|
|
应用程序B(无论本地还是远程)<-----
应用程序间的消息传递是通过队列来实现的,是间接的,所以即使B没能正常运行时,A仍然能正常运行,而且,消息还可以唤醒B。
可见,MQSeries的优点是可以确保信息是永久的、可恢复的,确保信息传递的安全性,确保信息的成功发送且只发送一次,同时,还可以使各应用程序可以独立的正常运行。
无论是共有的还是私有的队列,都是用MQOpenQueue()打开,MQOpenQueue函数用来打开一个用来发送消息或是读取消息的队列。它的原型是HRESULT
APIENTRY MQOpenQueue( LPCWSTR
lpwcsFormatName, DWORD
dwAccess, DWORD
dwShareMode, LPQUEUEHANDLE
phQueue
);其中,lpwcsFormatName是用户指定的指向队列的format name字符串,这个format
name可以是public,private,或者是direct
format。dwAccess是由用户指定的应用程序使用队列的方法,可以是peek,send,receive。当队列打开以后这个设置就不能被更
改。dwAccess有三种模式:MQ_PEEK_ACCESS/MQ_SEND_ACCESS/MQ_RECEIVE_ACCESS。
dwShareMode是由用户指定的队列的共享模式,分为:MQ_DENY_NONE和
MQ_DENY_RECEIVE_SHARE,MQ_DENY_NONE是默认值,允许任何人使用,如果dwAccess设置成了
MQ_SEND_ACCESS,那么在此就必须设置成MQ_DENY_NONE;MQ_DENY_RECEIVE_SHARE限制了从此队列读取消息的进
程。如果这个队列已经被别的进程打开用来接收消息,那么将返回
MQ_ERROR_SHARING_VIOLATION,MQ_DENY_RECEIVE_SHARE仅适用于当dwAccess设置成
MQ_RECEIVE_ACCESS或MQ_PEEK_ACCESS的时候。phQueue是输出的已打开的队列的句柄。
然后可以用MQSendMessage()/MQReceiveMessage()等函数进行具体操作,在此不多熬述。
C# 程序间通信的各种途径及解析的更多相关文章
-
Oracle的tnsnames.ora配置(PLSQL Developer)
首先打开tnsnames.ora的存放目录,一般为D:\app\Administrator\product\11.2.0\client_1\network\admin,就看安装具体位置了. 步骤阅读 ...
-
listener.ora/sqlnet.ora/tnsnames.ora配置文件详解
oracle网络配置 三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora ,都是放在$ORACLE_HOME/network/admin目录下. 英文说明: The ...
-
oracle连接的三个配置文件(转)
Oracle中TNS的完整定义:transparence Network Substrate透明网络底层,监听服务是它重要的一部分,不是全部,不要把TNS当作只是监听器 ORACLE当中 ...
-
ORACLE远程连接数据库
1. sqlplus sqlnet.ora 文件格式NAMES.DIRECTORY_PATH= (TNSNAMES,HOSTNAME).客户端就会首先在tnsnames.ora文件中找orcl的记录. ...
-
oracle的sqlnet.ora , tnsnames.ora , Listener.ora 文件的作用(转)
oracle网络配置三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora ,都是放在$ORACLE_HOME/network/admin目录下.1. sqlnet.o ...
-
oracle client server那点事
oracle网络配置三个配置文件 listener.ora.sqlnet.ora.tnsnames.ora ,都是放在$ORACLE_HOME\network\admin目录下. 1. sqlnet ...
-
涂抹Oracle笔记1-创建数据库及配置监听程序
一.安装ORACLE数据库软件及创建实例OLTP:online transaction processing 指那些短事务,高并发,读写频繁的数据库系统.--DB_BLOCK_SIZE通常设置较小.O ...
-
listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例(转载:http://blog.chinaunix.net/uid-83572-id-5510.ht)
listener.ora--sqlnet.ora--tnsnames.ora的关系以及手工配置举例 ====================最近看到好多人说到tns或者数据库不能登录等问题,就索性总结 ...
-
Oracle的网络监听配置
listener.ora.tnsnames.ora和sqlnet.ora这3个文件是关系oracle网络配置的3个主要文件,都是放在$ORACLE_HOME\network\admin目录下.其中li ...
随机推荐
-
TCP连接建立和终止小结
TCP连接建立(三次握手) 如图: 请求端发送一个SYN到服务器的相应端口,以及初始序号ISN 服务器发送包含服务器的初始序号的SYN作为应答,同时确认序号设置为客户的ISN+1 客户将确认序号设置为 ...
- NPM 如何升级?
-
51nod 1116 K进制下的大数 (暴力枚举)
题目链接 题意:中文题. 题解:暴力枚举. #include <iostream> #include <cstring> using namespace std; ; ; ch ...
-
iBatis.net入门指南
iBatis.net入门指南 - 1 - 什么是iBatis.net ? - 3 - iBatis.net的原理 - 3 - 新人指路 - 3 - iBatis.net的优缺点 ...
-
Windows2000安装Winform Clickonce提示升级系统版本的解决方案
Windows2000安装Winform Clickonce提示升级系统版本.只需要把所有应用的DLL的独立性设置为false就可以了.
-
python数据结构(二)------列表
本文将重点梳理列表及列表操作. 2.1 list函数 2.2 基本列表操作 2.3 列表方法 2.1 list函数 >>>list('hello') ['h','e','l',l', ...
-
day30 小面试题 去重 (考核 __eq__ 以及 __hash__ )
# 小面试题,要求将一个类的多个对象进行去重 # 使用set方法去重,但是无法实现,因为set 需要依赖eq以及hash, # hash 哈希的是内存地址, 必然不一样 # eq 比较的也是内存地址, ...
-
io多路复用的精髓
前言 当我们要编写一个echo服务器程序的时候,需要对用户从标准输入键入的交互命令做出响应.在这种情况下,服务器必须响应两个相互独立的I/O事件:1)网络客户端发起网络连接请求,2)用户在键盘上键入命 ...
-
sqlserver安装遇到的问题——1
SQL Server安装过无数次,今天第一次遇到这样的问题 一.问题消息复制出来是这样的 TITLE: Microsoft SQL Server 2008 R2 安装程序--------------- ...
-
linux设置定时任务调用接口
1.设置目录 cd /var/spool/cron 2.编辑文件(当前登录用户,不一定是root) vim root 3.添加内容 0 0 * * * wget http://192.144.141. ...