08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活。此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net/xiaowei_cqu/article/details/7747205
进程间通信
命名管道
进程间通信的一种方式,Pipes:管道,分为无名管道:在父子进程间交换数据;有名管道:可在不同主机间交换数据,分为服务器方和客户方,在Win9X下只支持有名管道客户。
命名管道的命名
命名管道是一个有名字的,单向或双向的通信管道。管道的名称有两部分组成:计算机名和管道名,例如\\[host_name]\pipe\[pipe_name]\(括号内为参数)。对于同一主机来讲允许有多个同一命名管道的实例并且可以由不同的进程打开,但是不同的管道都有属于自己的管道缓冲区而且有自己的通讯环境互不影响,并且命名管道可以支持多个客户端连接一个服务器端。命名管道客户端不但可以与本机上的服务器通讯也可以同其他主机上的服务器通讯。
命名管道的连接
在服务器端第一次创建命名管道后等待连接,当客户端连接成功后服务器端的命名管道就用作通讯用途。如果需要再次等待连接,服务器端就需要再次打开命名管道(创建一个命名管道的实例)并等待连接。
对于客户端每次打开命名管道后建立与服务器间的连接,然后就可以利用命名管道进行通信,如果需要建立第二个连接则需要再次打开管道和再次建立连接。
创建命名管道时需要指定一个主机名和管道名,对于客户端来说可以是如下格式:\\[host_name]\pipe\[pipe_name]\也可以是\\.\pipe\pipe_name\其中.表示本机。而服务器端只能够在指定本机作为主机名,即只能使用下面的格式:\\.\pipe_name\。此外需要记住,在同一主机上管道名称是唯一的,一个命名管道一旦被创建就不允许相同名称的管道再被创建。
主要函数
管道服务器首次调用CreateNamedPipe()函数时,使用nMaxInstance参数指定了能同时存在的管道实例的最大数目。服务器可以重复调用CreateNamedPipe()函数去创建管道新的实例,直至达到设定的最大实例数。
服务器方通过该函数创建命名管道和打开已经存在的命名管道,其中lpName为管道名称,dwOpenMode为创建方式,可以是下面值的组合:
PIPE_ACCESS_INBOUND:管道只能用作接收数据。
PIPE_ACCESS_OUTBOUND:管道只能用作发送数据。
PIPE_ACCESS_DUPLEX:管道既可以发送也可以接收数据。(上面这三个值只能够取其中一个)
FILE_FLAG_WRITE_THROUGH:管道用于同步发送和接收数据,只有在数据被发送到目标地址时发送函数才会返回,如果不设置这个参数那么在系统内部对于命名管道的处理上可
能会因为减少网络附和而在数据积累到一定量时才发送,并且对于发送函数的调用会马上返回。
管道的连接管理,客户方在调用CreateFile后立即就能够建立服务器的连接,而服务器方一旦管道打开或创建后可以用
BOOL ConnectNamedPipe(
HANDLE hNamedPipe, // handle to named pipe
LPOVERLAPPED lpOverlapped // overlapped structure
);
来等待客户端的连接建立。如果希望在服务器方检测是否有连接到达,可以调用
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName, // pipe name
DWORD nTimeOut // time-out interval
);
这里的lpNamePipeName直接使用创建管道时的名称,如果在服务器方希望关闭连接则调用
BOOL DisconnectNamedPipe(
HANDLE hNamedPipe // handle to named pipe
);
一旦连接被关闭,服务器方可以再次调用ConnectNamedPipe来建立连接。如果要关闭管道则直接调用CloseHandle。请注意这里提到的关闭管道和关闭连接是不同的意思,在同一个管道上可以依次反复建立连接,而且可以减小系统的负荷。而且如果指定了管道最大数量限制那么在打开的管道达到最大限制后如果不关闭旧管道就无法打开新管道。 对于客户方则无法关闭连接,而只能直接调用CloseHandle关闭管道。
数据的发送,不论是服务器还是客户方都可以通过ReadFile和WriteFile进行管道读写来达到通讯的目的。
【实验说明】
在第一次实验的基础上,进行进程通信实验,用管道方式在两个进程间进行通信,要求能传递一个数据结构,结构如下:
struct
{
int [9][9];
byte[16];
string;
}
不管用什么方式进行编码和解码,需要两个进程能够相互传递
【实验步骤】
1.查阅有关进程间通信的资料
用管道实现进程间通信,需要编写服务器及客户端。
服务器端设计:
客户端设计:
namespace MyPipe
{
class Program
{
static int numThreads = 2;
static void Main(string[] args)
{
Thread newThread = new Thread(ServerThread);
newThread.Start();
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
private static void ServerThread(object data)
{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);
Console.WriteLine("NamedPipeServerStream thread created.");
//等待客户端连接
pipeServer.WaitForConnection();
Console.WriteLine("Client connected.");
try
{
StreamReader sr = new StreamReader(pipeServer);
StreamWriter sw = new StreamWriter(pipeServer);
sw.AutoFlush = true;
//客户端通过此消息进行确认
sw.WriteLine("My Server!");
// Obtain the filename from the connected client.
string content = sr.ReadLine();
Console.WriteLine("Reading {0}", content);
pipeServer.Disconnect();
}
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Close();
}
}
}
3.客户端代码
在实验一的基础上,客户端使用两个线程,一个线程中实例一个客户端,使用管道通信向客户端发送数据(数据结构转为string类型);另一个线程中接收服务器端管道中的数据,再将string转为定义的数据结构。
//定义要传递的数据结构
PassStruct myPass = new PassStruct(
new int[,]{
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9},
{1,2,3,4,5,6,7,8,9}
},
new byte[16] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7 },
"Authror:小魏");
private void 打开aToolStripMenuItem_Click(object sender, EventArgs e)
{
A = new Thread(new ThreadStart(exePa));
A.IsBackground = true;
A.Start();
}
//声明一个委托,用以解决控件绑定到特定线程抛出的InvalidOperationException异常
delegate void setRtbHandler(string s);
private void setRtb(string s)
{
tabPage2.Controls[0].Text += s;
}
/// <summary>
/// 进程Pa
/// </summary>
private void exePa()
{
////原本用作测试的
//this.tabPage2.Controls[0].Text = "aaa";
info += "线程A(写入线程)打开\n";
try{
//这里第一个参数是我的计算机名
NamedPipeClientStream pipeClientA =
new NamedPipeClientStream("WEI-THINKPAD", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);
StreamWriter sw = new StreamWriter(pipeClientA);
StreamReader sr = new StreamReader(pipeClientA);
pipeClientA.Connect();
sw.AutoFlush = true;
//确认服务器连接
if (sr.ReadLine() == "My Server!")
{
//向管道中写入数据(先转化为字符串)
toWrite = StructToString(myPass);
sw.Write(toWrite);
}
else
{
info +="Server could not be verified.\n";
}
//关闭客户端
pipeClientA.Close();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
/// 关闭A
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void 关闭AToolStripMenuItem_Click(object sender, EventArgs e)
{
if (A.IsAlive)
{
info += "线程A(写入线程)已关闭\n";
A.Abort();
}
}
private void 打开aToolStripMenuItem1_Click(object sender, EventArgs e)
{
B = new Thread(new ThreadStart(exePb));
B.IsBackground = true;
B.Start();
}
/// <summary>
/// 线程B
/// </summary>
private void exePb()
{
info += "线程B(读出线程)打开\n";
try
{
NamedPipeClientStream pipeClientB =
new NamedPipeClientStream("WEI-THINKPAD", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);
StreamWriter sw = new StreamWriter(pipeClientB);
StreamReader sr = new StreamReader(pipeClientB);
pipeClientB.Connect();
sw.AutoFlush = true;
if (sr.ReadLine() == "My Server!")
{
PassStruct getPass = StringToStruct(toWrite);
string structToShow="";
//将读到的数据结构以一定的格式显示到屏幕上
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
structToShow += getPass.arrayInt[i, j].ToString() + " ";
}
structToShow += "\n";
}
for (int k = 0; k < 16; k++)
structToShow += getPass.arrayByte[k].ToString() + " ";
structToShow += "\n";
structToShow += getPass.ss;
structToShow += "\n";
info += structToShow;
}
else
{
info += "Server could not be verified.\n";
}
pipeClientB.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/// <summary>
///关系线程B
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void 关?闭À?ToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (B.IsAlive)
{
B.Abort();
A.Abort();
info += "线程B(读出线程)已关闭\n";
this.tabPage2.Controls[0].Text = info;
}
}
4.数据结构与string相互转化的函数
//数据结构转为字符串(所有的数字变为字符串,用“,”隔开)
public string StructToString(PassStruct ps)
{
string s = "";
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
s =s+(ps.arrayInt[i, j]).ToString() + ",";
for (int k = 0; k < 16; k++)
s = s + (ps.arrayByte[k]).ToString() + ",";
s = s + ps.ss;
return s;
}
//将string转为定义的数据结构
public PassStruct StringToStruct(string s)
{
int[,] x = new int[9, 9];
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
int p = s.IndexOf(',');// 通过‘,’找到分割
string tmp = s.Substring(0, p);// 截取‘,’之前的部分转为int
x[i, j] = int.Parse(tmp);
s = s.Remove(0, p + 1);// 通过Remove移除已转为int的部分
}
}
//同样的方法得到byte部分
byte[] y = new byte[16];
for (int k = 0; k < 16; k++)
{
int p = s.IndexOf(',');
string tmp = s.Substring(0, p);
y[k] = byte.Parse(tmp);
s = s.Remove(0, p + 1);
}
//剩下的部分为结构中字符串的部分
PassStruct getPass = new PassStruct(x, y, s);
return getPass;
}
【实验结果】
服务器端输出截图:
客户端截图:
【实验中遇到的问题】
实验的大部分地方都是用try catch来处理异常,catch中通过MessageBox显示可以很快看到错误问题。实验中遇到:
网上查阅了一些资料,了解信号灯也是进程间通信的一种方式。“信号灯与其它进程间通信方式有所不同,它主要用于进程间同步。通常所说的系统V信号灯实际上是一个信号灯的集合,可用于多种共享资源的进程间同步。每个信号灯都有一个值,可以用来表示当前该信号灯代表的共享资源可用(available)数量,如果一个进程要申请共享资源,那么就从信号灯值中减去要申请的数目,如果当前没有足够的可用资源,进程可以睡眠等待,也可以立即返回。”(http://www.cnblogs.com/thinkingworld/articles/1861739.html)
不少人遇到过这个故障(数据库中或硬盘读盘),但最终还是没有读懂,不知道自己为什么会遇到这个问题。猜测可能还是不同线程向窗口写入东西(改变空间属性)引起的问题。于是将输出的信息改为一次性输出,暂时没有再出现问题。(具体见代码中注释)
程序源码及实验报告文档下载:http://download.csdn.net/detail/xiaowei_cqu/3880949
分享到:
相关推荐
操作系统实验之进程间通信(管道方式)。 内涵实验源码及报告文档。 更详尽内容请参考小魏博客:http://blog.csdn.net/xiaowei_cqu/article/details/7041212
利用共享内存实现进程间的通信,可用于操作系统的教学。(原创)
进程是操作系统的基础之一。一个进程可以认为是一个正在执行的程序。我们可以把进程当做计算机运行时的一个基础单位。关于进程的讨论已经超出了本章的范畴,现在我们假定你是了解这个概念的。 在 Qt 中,我们使用...
套接字(Socket)是支持TCP/IP协议(安全)和UDP协议(快速)的网络通信的基本操作单元。套接字包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地...
进程间通讯(两个应用程序如何通讯)C#源代码 消息传递 message-passing:通过操作系统的相应系统调用进行消息传递通讯。分为直接和间接两种: 直接通信方式:点到点的发送 Send (DestProcessName, Message); ...
本文实例讲述了C#操作windows系统进程的方法。分享给大家供大家参考。具体如下: 这段代码演示了如何根据进程名关闭进程和启动进程 /// /// 关闭进程 /// private bool CloseProcess(string CloseProcessName) { ...
显然第3种做法更地道,实现该效果的核心问题其实是:如何显示指定进程的窗口? 首先想到的是调用ShowWindow、SetForegroundWindow等API,配合使用可以将被遮挡、最小化的窗口前排显示出来,这也是很多涉及到这种...
如果不采用进程间通信(RPC)机制,则在一个进程中执行的代码就不能访问另一进程。这是一种操作系统对应用程序的保护机制。然而在某些情况下,我们需要跨过应用程序域,与另外的应用程序域进行通信,即穿越边界。 在...
操作系统:LInux、树莓派、安卓开发、微机操作系统、网络操作系统、分布式操作系统等。此外,还有嵌入式操作系统、智能操作系统等。 网络与通信:数据传输、信号处理、网络协议、网络与通信硬件、网络安全网络与通信...
果不采用进程间通信(RPC)机制,则在一个进程中执行的代码就不能访问另一进程。这是 一种操作系统对应用程序的保护机制。然而在某些情况下,我们需要跨过应用程序域,与另 外的应用程序域进行通信,即穿越边界。
进程间 Cloudtoid Interprocess是跨平台的共享内存队列,用于进程之间的快速通信( )。 它使用共享的内存映射文件在进程之间进行极其快速和高效的通信,并且在Microsoft内部使用。 :非常快。 跨平台:它支持...
毕设&课设&项目&实训-基于STM32F103ZET6移植ucos操作系统,实现最基本的进程调度和进程间通信;其它功能尚未测试。 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、...
关于阿巴努 ...基本的进程间通信 贡献 请随时与我们联系,打开问题或请求请求。 执照 Abanu是根据MIT许可发布的。 该软件包括第三方开源软件组件。 这些软件组件均具有自己的许可证。 所有源代码都是开源的。
第一部分 C#语言概述.4 第一章 第一章第一章 第一章 .NET 编 编 编程语言 程语言编程语言 程语言 C#.4 1.1 Microsoft.NET——一场新的革命.4 1.2 .NET 与 C#.6 1.3 C#语言的特点.8 1.4 小 结 .11...
lock和Monitor是.NET用一个特殊结构实现的,Monitor对象是完全托管的、完全可移植的,并且在操作系统资源要求方 面可能更为有效,同步速度较快,但不能跨进程同步。lock(Monitor.Enter和Monitor.Exit方法的封装)...
9.2.3 窗体间的互操作 205 9.2.4 锁定模块主窗体 206 9.2.5 使用ADO.NET连接Access数据库 206 9.3 设计过程 207 9.3.1 数据库设计 207 9.3.2 群发短信实现 209 9.3.3 已发送短信管理 213 9.3.4 接收...
WCF 概括地说,WCF具有如下的优势: 1、统一性 前面已经叙述,WCF是对于ASMX,.Net Remoting,Enterprise ...应用程序可以运行在Windows操作系统下,也可以运行在其他的操作系统,如Sun Solaris,HP Unix,Linux等等
9.2.3 窗体间的互操作 9.2.4 锁定模块主窗体 9.2.5 使用ADO.NET连接Access数据库 9.3 设计过程 9.3.1 数据库设计 9.3.2 群发短信实现 9.3.3 已发送短信管理 9.3.4 接收短信实现 9.3.5 常用联系人管理 9.3.6 常用短语...
273 实例190 获取窗口文本 273 实例191 判断文件是否正在被使用 274 实例192 在程序中调用.HLP文件 275 实例193 C#中实现文件拖放 276 实例194 文件比较 276 第7章 操作系统与Windows...
9.2.3 窗体间的互操作 9.2.4 锁定模块主窗体 9.2.5 使用ADO.NET连接Access数据库 9.3 设计过程 9.3.1 数据库设计 9.3.2 群发短信实现 9.3.3 已发送短信管理 9.3.4 接收短信实现 9.3.5 常用联系人管理 9.3.6 常用短语...