Thank Swarajya Pendharkar and MSDN very much
进程间通信
进程基本概念
In computer science, inter-process communication or interprocess communication (IPC) refers specifically to the mechanisms an operating system provides to allow processes it manages to share data. Typically, applications can use IPC categorized as clients and servers, where the client requests data and the server responds to client requests.Many applications are both clients and servers, as commonly seen in distributed computing. Methods for achieving IPC are divided into categories which vary based on software requirements, such as performance and modularity requirements, and system circumstances, such as network bandwidth and latency.
Approaches
Method | Short Description | Provided by (operating systems or other environments) |
---|---|---|
File | A record stored on disk, or a record synthesized on demand by a file server, which can be accessed by multiple processes. | Most operating systems |
Signal; also Asynchronous System Trap | A system message sent from one process to another, not usually used to transfer data but instead used to remotely command the partnered process. | Most operating systems |
Socket | A data stream sent over a network interface, either to a different process on the same computer or to another computer on the network. Typically byte-oriented, sockets rarely preserve message boundaries. Data written through a socket requires formatting to preserve message boundaries. | Most operating systems |
Message queue | A data stream similar to a socket, but which usually preserves message boundaries. Typically implemented by the operating system, they allow multiple processes to read and write to the message queue without being directly connected to each other. | Most operating systems |
Pipe | A unidirectional data channel. Data written to the write end of the pipe is buffered by the operating system until it is read from the read end of the pipe. Two-way data streams between processes can be achieved by creating two pipes utilizing standard input and output. | All POSIX systems, Windows |
Named pipe | A pipe implemented through a file on the file system instead of standard input and output. Multiple processes can read and write to the file as a buffer for IPC data. | All POSIX systems, Windows, AmigaOS 2.0+ |
Semaphore | A simple structure that synchronizes multiple processes acting on shared resources. | All POSIX systems, Windows, AmigaOS |
Shared memory | Multiple processes are given access to the same block of memory which creates a shared buffer for the processes to communicate with each other. | All POSIX systems, Windows |
Message passing | Allows multiple programs to communicate using message queues and/or non-OS managed channels, commonly used in concurrency models. | Used in RPC, RMI, and MPI paradigms, Java RMI, CORBA, DDS, MSMQ, MailSlots, QNX, others |
Memory-mapped file | A file mapped to RAM and can be modified by changing memory addresses directly instead of outputting to a stream. This shares the same benefits as a standard file. | All POSIX systems, Windows |
进程间通信(IPC)实现
The following IPC mechanisms are supported by Windows:
- Clipboard
- COM
- Data Copy
- DDE
- File Mapping
- Mailslots
- Pipes
- RPC
- Windows Sockets
进程间通信之共享内存实现
File mapping enables a process to treat the contents of a file as if they were a block of memory in the process’s address space. The process can use simple pointer operations to examine and modify the contents of the file. When two or more processes access the same file mapping, each process receives a pointer to memory in its own address space that it can use to read or modify the contents of the file. The processes must use a synchronization object, such as a semaphore, to prevent data corruption in a multitasking environment.
Following are some of the Win32 APIs that are used when working with shared memory (memory mapped objects):
- CreateFileMapping()
- MapViewOfFile()
- UnMapViewOfFile()
- CloseHandle()
Key Point: Mailslots offer an easy way for applications to send and receive short messages. They also provide the ability to broadcast messages across all computers in a network domain.
#include <iostream>
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
using namespace std;
//-------------------------------
#define MAX_SH_MEM_SIZE 1024
#define MAX_READ_PROCESSES_ALLOWED 3
#define WAIT_TIME_OUT 100
//-------------------------------
//-------------------------------
/*
Global variables
*/
HANDLE g_hReadEvent[MAX_READ_PROCESSES_ALLOWED];
TCHAR g_szReadEventName[] = _T("My Read Event");
HANDLE g_hWriteEvent = NULL;
TCHAR g_szWriteEventName[] = _T("My Write Event");
HANDLE g_hSharedMemory = NULL;
LPTSTR g_pBuffer = NULL;
TCHAR g_szSharedMemoryName[] = _T("My Shared Memory");
bool Initialize();
void DeInitialize();
void DisplayOptions();
bool RecvAndProcessOption();
void WriteAndPrint();
void ReadAndPrint();
int main(int argv, char* argc[], char* env[]){
if (Initialize()){
std::cout << "Initialization of process was successful" << endl;
}
else
{
DeInitialize();
std::cout << "Initialization of the process was not successful" << endl;
return 1;
}
bool bContinue = true;
while (bContinue){
DisplayOptions();
bContinue = RecvAndProcessOption();
}
DeInitialize();
}
bool Initialize(){
for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
{
g_hReadEvent[i] = NULL;
}
TCHAR szBuffer[32];
/*
原型
int sprintf( char *buffer, const char *format, [ argument] … );
参数列表
buffer:char型指针,指向将要写入的字符串的缓冲区。
format:格式化字符串。
[argument]...:可选参数,可以是任何类型的数据。
返回值
返回写入buffer 的字符数,出错则返回-1. 如果 buffer 或 format 是空指针,且不出错而继续,函数将返回-1,并且 errno 会被设置为 EINVAL。
sprintf 返回被写入buffer 的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的buffer后,函数sprintf 返回5。
*/
for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
{
_stprintf_s(szBuffer, _T("%s %d"), g_szReadEventName, i);
g_hReadEvent[i] = CreateEvent(NULL, false, true, szBuffer);
if (NULL == g_hReadEvent[i]){
return false;
}
}
/*
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes,// 安全属性
BOOLbManualReset,// 复位方式
BOOLbInitialState,// 初始状态
LPCTSTRlpName // 对象名称 事件对象的名称
);
如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。
*/
g_hWriteEvent = CreateEvent(NULL, false, true, g_szWriteEventName);
if (NULL == g_hWriteEvent){
return false;
}
/*函数原型
HANDLE WINAPI CreateFileMapping(
_In_HANDLE hFile,
_In_opt_LPSECURITY_ATTRIBUTES lpAttributes,
_In_DWORD flProtect,
_In_DWORD dwMaximumSizeHigh,
_In_DWORD dwMaximumSizeLow,
_In_opt_LPCTSTR lpName);
创建一个新的文件映射内核对象。
*/
g_hSharedMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MAX_SH_MEM_SIZE, g_szSharedMemoryName);
if (NULL == g_hSharedMemory || INVALID_HANDLE_VALUE == g_hSharedMemory){
std::cout << "Error occured while creating file mapping object:" << GetLastError() << endl;
return false;
}
/*
函数原型
LPVOID WINAPI MapViewOfFile(
__in HANDLE hFileMappingObject,
__in DWORD dwDesiredAccess,
__in DWORD dwFileOffsetHigh,
__in DWORD dwFileOffsetLow,
__in SIZE_T dwNumberOfBytesToMap
);
MapViewOfFile,计算机语言。将一个文件映射对象映射到当前应用程序的地址空间。MapViewOfFileEx允许我们指定一个基本地址来进行映射。
参数1:hFileMappingObject 为CreateFileMapping()返回的文件映像对象句柄。
参数2:dwDesiredAccess 映射对象的文件数据的访问方式,而且同样要与CreateFileMapping()函数所设置的保护属性相匹配。 可取以下值:
FILE_MAP_ALL_ACCESS 等价于CreateFileMapping的 FILE_MAP_WRITE|FILE_MAP_READ. 文件映射对象被创建时必须指定PAGE_READWRITE 选项.
FILE_MAP_COPYA 可以读取和写入文件.写入操作会导致系统为该页面创建一份副本.在调用CreateFileMapping时必须传入PAGE_WRITECOPY保护属性.
FILE_MAP_EXECUTE 可以将文件中的数据作为代码来执行.在调用CreateFileMapping时可以传入PAGE_EXECUTE_READWRITE或PAGE_EXECUTE_READ保护属性.
FILE_MAP_READ 可以读取文件.在调用CreateFileMapping时可以传入PAGE_READONLY或PAGE_READWRITE保护属性.
FILE_MAP_WRITEA 可以读取和写入文件.在调用CreateFileMapping时必须传入PAGE_READWRITE保护属性.
参数3:dwFileOffsetHigh 表示文件映射起始偏移的高32位.
参数4:dwFileOffsetLow 表示文件映射起始偏移的低32位.(64KB对齐不是必须的)
参数5:dwNumberOfBytes 指定映射文件的字节数.
*/
g_pBuffer = (LPTSTR)MapViewOfFile(g_hSharedMemory, FILE_MAP_ALL_ACCESS, 0, 0, MAX_SH_MEM_SIZE);
if (NULL == g_pBuffer){
std::cout << "Error occured while mapping view of the file:" << GetLastError() << endl;
return false;
}
return true;
}
void DeInitialize(){
for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
{
CloseHandle(g_hReadEvent[i]);
}
CloseHandle(g_hWriteEvent);
UnmapViewOfFile(g_pBuffer);
CloseHandle(g_hSharedMemory);
}
void DisplayOptions(){
cout << "--------------------------------------------------------------" << endl;
cout << "------------Operation on Share Memory--(Read/Write)-----------" << endl;
cout << "------------Enter 1 to write to the shared memory-------------" << endl;
cout << "------------Enter 2 to read the shared memory-----------------" << endl;
cout << "------------Enter 3 to exit the application-------------------" << endl;
cout << "--------------------------------------------------------------" << endl;
cout << "Enter option:";
}
bool RecvAndProcessOption(){
int nInput;
bool bReturnValue = true;
cin >> nInput;
switch (nInput){
case 1:
cout << "Write Operation selected" << endl;
WriteAndPrint();
bReturnValue = true;
break;
case 2:
cout << "Read Operation selected" << endl;
ReadAndPrint();
bReturnValue = true;
break;
case 3:
cout << "Quit Operation selected" << endl;
bReturnValue = false;
break;
default:
cout << "Invalid Operation selected" << endl;
bReturnValue = true;
break;
}
return bReturnValue;
}
void WriteAndPrint(){
cout << "Trying to write operation to complete..." << endl;
/**
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
hHandle[in]对象句柄。可以指定一系列的对象,如Event、Job、Memory resource notification、Mutex、Process、Semaphore、Thread、Waitable timer等。
dwMilliseconds[in]定时时间间隔,单位为milliseconds(毫秒).
如果指定一个非零值,函数处于等待状态直到hHandle标记的对象被触发,或者时间到了。
如果dwMilliseconds为0,对象没有被触发信号,函数不会进入一个等待状态,它总是立即返回。
如果dwMilliseconds为INFINITE,对象被触发信号后,函数才会返回。
返回值:
WAIT_ABANDONED 0x00000080:当hHandle为mutex时,如果拥有mutex的线程在结束时没有释放核心对象会引发此返回值。
WAIT_OBJECT_0 0x00000000 :指定的对象表现为有信号状态
WAIT_TIMEOUT 0x00000102:等待超时
WAIT_FAILED 0xFFFFFFFF :出现错误,可通过GetLastError得到错误代码
*/
if (WAIT_OBJECT_0 == WaitForSingleObject(g_hWriteEvent, INFINITE)){
cout << "Waiting for all read Operations to complete..." << endl;
/*
原型:DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
如果函数成功,返回值表示该事件导致该函数返回。这个值可以是下列之一。
ValueMeaning
WAIT_OBJECT_0到(WAIT_OBJECT_0 + nCount - 1如果bWaitAll为TRUE),则返回值表明所有指定对象的状态信号。
如果bWaitAll为FALSE,则返回值减去不是WAIT_OBJECT_0表示lpHandles数组的对象的满意指数的等待。如果多个对象在通话过程中信号成为,这是与所有的信号对象的最小索引值的信号对象的数组索引。
WAIT_ABANDONED_0至(WAIT_ABANDONED_0 + nCount - 1)如果bWaitAll为TRUE,则返回值表明所有指定对象的状态是触发的,并且至少对象之一,是一个废弃的互斥对象。
If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait
如果bWaitAll为FALSE,则返回值减去WAIT_ABANDONED_0 表示一个废弃的互斥对象在lpHandles数组中的下标,满足等待。
WAIT_TIMEOUTThe超时间隔已过,由bWaitAll参数指定的条件得不到满足。
*/
DWORD dwWaitReadResult = WaitForMultipleObjects(MAX_READ_PROCESSES_ALLOWED, g_hReadEvent, TRUE, INFINITE);
if (WAIT_OBJECT_0 == dwWaitReadResult){
cout << "Enter a string (without spaces):";
char c_text[100];
//cin >> strText;
//gets(c_text);
fflush(stdin); //清空输入缓冲区 c++
/**
stdin
std = stand in = input
stdin 就是标准输入设备,一般地就是键盘了
stdin是重向定义,
定义为标准输入设备(即健盘)
stdout
标准输出设备(显示器)
函数原型
char *fgets(char *buf, int bufsize, FILE *stream);
参数
*buf: 字符型指针,指向用来存储所得数据的地址。
bufsize: 整型数据,指明存储数据的大小。
*stream: 文件结构体指针,将要读取的文件流。
*/
fgets(c_text, 100, stdin);
//g_pBuffer=(LPTSTR)(LPCSTR)c_text;
CopyMemory(g_pBuffer, c_text, strlen(c_text));
cout << "Shared Memory:" << g_pBuffer << endl;
}
else{
cout << "Error occured while waiting:" << GetLastError() << endl;
}
cout << "Setting the Write Event..." << endl;
SetEvent(g_hWriteEvent);
cout << "Setting the Read Events..." << endl;
for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
{
SetEvent(g_hReadEvent[i]);
}
}
else{
cout << "Error occured while waiting:"<<GetLastError() << endl;
}
}
void ReadAndPrint(){
cout << "Trying to read and print the shared memory..." << endl;
bool bContinue = true;
while (bContinue){
cout << "Waiting for write operation to complete..." << endl;
DWORD dwWaitResult = WaitForSingleObject(g_hWriteEvent, INFINITE);
if (WAIT_OBJECT_0 == dwWaitResult){
bool bEventFound = false;
for (int i = 0; i < MAX_READ_PROCESSES_ALLOWED; i++)
{
DWORD dwWaitResult = WaitForSingleObject(g_hReadEvent[i], WAIT_TIME_OUT);
if (WAIT_OBJECT_0 == dwWaitResult){
bEventFound = true;
cout << "Setting the Write Event..." << endl;
SetEvent(g_hWriteEvent);
cout << "Setting the Write Event..." << endl;
SetEvent(g_hWriteEvent);
cout << "Shared Memory:" << g_pBuffer << endl;
cout << "Setting the Read Event..." << endl;
SetEvent(g_hReadEvent[i]);
bContinue = false;
break;
}
else
{
continue;
}
}
if (false == bEventFound){
cout << "Setting the Write Event..." << endl;
SetEvent(g_hWriteEvent);
Sleep(WAIT_TIME_OUT);
}
}
else
{
cout << "Error occured while waiting:"<<GetLastError() << endl;
}
}
}
- Source code is platform dependent.
- Can only be used for processes that are on the same computer.
- Once a memory mapped object is mapped into the process area, using it is very straightforward and easy.
- Perhaps the fastest IPC mechanism.
进程间通信之命名管道实现
There are two types of pipes for two-way communication: anonymous pipes and named pipes.
Anonymous pipes enable related processes to transfer information to each other. Typically, an anonymous pipe is used for redirecting the standard input or output of a child process so that it can exchange data with its parent process. To exchange data in both directions (duplex operation), you must create two anonymous pipes. The parent process writes data to one pipe using its write handle, while the child process reads the data from that pipe using its read handle. Similarly, the child process writes data to the other pipe and the parent process reads from it. Anonymous pipes cannot be used over a network, nor can they be used between unrelated processes.
Named pipes are used to transfer data between processes that are not related processes and between processes on different computers. Typically, a named-pipe server process creates a named pipe with a well-known name or a name that is to be communicated to its clients. A named-pipe client process that knows the name of the pipe can open its other end, subject to access restrictions specified by named-pipe server process. After both the server and client have connected to the pipe, they can exchange data by performing read and write operations on the pipe.
Key Point: Anonymous pipes provide an efficient way to redirect standard input or output to child processes on the same computer. Named pipes provide a simple programming interface for transferring data between two processes, whether they reside on the same computer or over a network.
Pipes are FIFO in behavior (First-In, First-Out). Pipes are of two types – Anonymous Pipes and Named Pipes.
An Anonymous Pipe is created using the CreatePipe() API. An anonymous pipe is local, and cannot be used to communicate over a network. An anonymous pipe is unnamed, one-way, and generally used to transfer data from a parent process to a child process.
Named pipes can be one-way or duplex. Named pipes will work between processes on the same computer as well as between processes across the network.
Following are some of the Win32 APIs that are used when working with Named Pipes:
- CreateNamedPipe()
- ConnectNamedPipe()
- WaitNamedPipe()
- DisconnectNamedPipe()
- ReadFile()
- WriteFile()
- CloseHandle()
A Named Pipe name needs to be in the following format:
- For named pipe server - \.\pipe\PipeName
- For named pipe client - \ComputerName\pipe\PipeName
Source Code Of Client:
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
/*
1. [CreateFile](http://baike.baidu.com/view/1288759.htm)
*/
LPTSTR g_szPipeName = _T("\\\\.\\Pipe\\MyNamedPipe");
#define BUFFER_SIZE 1024
#define ACK_MESG_REVE "Message received successfully"
int main(int argv,char* argc[],char* env[]){
HANDLE hPipe;
/*
HANDLE WINAPI CreateFile(
_In_ LPCTSTR lpFileName,//普通文件名或者设备文件名
_In_ DWORD dwDesiredAccess, //访问模式(写/读)
_In_ DWORD dwShareMode,//共享模式
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
_In_ DWORD dwCreationDisposition, //如何创建
_In_ DWORD dwFlagsAndAttributes, //文件属性
_In_opt_ HANDLE hTemplateFile //用于复制文件句柄
);
*/
hPipe = CreateFile(g_szPipeName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (INVALID_HANDLE_VALUE == hPipe){
printf("\nError occurred while connecting to the server: %d",GetLastError());
return 1;
}
else{
printf("\nCreateFile() was successful.\n");
}
char szBuffer[BUFFER_SIZE];
printf("\nEnter a message to be sent to the server\n");
gets_s(szBuffer);
DWORD cbBytes;
BOOL bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
printf("\n Error occurred while writing to the server: %d\n", GetLastError());
CloseHandle(hPipe);
return 1;
}
else{
printf("\nWriteFile() was successful..\n");
}
bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer),&cbBytes, NULL);
if ((!bResult) || (0 == cbBytes)){
printf("\n Error occurred while reading from the server: %d\n", GetLastError());
CloseHandle(hPipe);
return 1;
}
else{
printf("\nReadFile() was successful...\n");
}
printf("\nServer sent the following message:%s\n",szBuffer);
CloseHandle(hPipe);
system("pause");
return 0;
}
Source Code Of Server:
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
TCHAR g_szPipeName[] = _T("\\\\\.\\Pipe\\MyNamedPipe");
#define BUFFER_SIZE 1024
#define ACK_MESG_RECV "Message reveived successfully..."
int main(int argc, char* argv[], char* env[]){
HANDLE hPipe;
/**
HANDLE WINAPI CreateNamedPipe(
_In_ LPCTSTR lpName,
_In_ DWORD dwOpenMode,
_In_ DWORD dwPipeMode,
_In_ DWORD nMaxInstances,
_In_ DWORD nOutBufferSize,
_In_ DWORD nInBufferSize,
_In_ DWORD nDefaultTimeOut,
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
CreateNamedPipe创建一个命名管道。返回的句柄由管道的服务器端使用
说明
返回值
Long,如执行成功,返回管道的句柄。INVALID_HANDLE_VALUE表示失败。会设置GetLastError
参数表
参数 类型及说明
lpName String,指定管道名,采用的形式是:\\.\管道\管道名。最多可达256个字符的长度,而且不用区分大小写。如果存在指定名字的一个管道,则创建那个管道的一个新实例
dwOpenMode Long,下述常数组的一个组合
下述常数之一(对于管道的所有实例都要一样):
PIPE_ACCESS_DUPLEX 管道是双向的
PIPE_ACCESS_INBOUND 数据从客户端流到服务器端
PIPE_ACCESS_OUTBOUND 数据从服务器端流到客户端
下述常数的任意组合
FILE_FLAG_WRITE_THROUGH 在网络中建立的字节型管道内,强迫数据在每次读写操作的时候通过网络传输。否则传输就可能延迟
FILE_FLAG_OVERLAPPED 允许(但不要求)用这个管道进行异步(重叠式)操作
常数WRITE_DAC, WRITE_OWNER 和 ACCESS_ SYSTEM_SECURITY提供了附加的安全选项
dwPipeMode Long,下述常数组的一个组合:
下述常数之一(管道的所有实例都必须指定相同的常数)
PIPE_TYPE_BYTE 数据作为一个连续的字节数据流写入管道
PIPE_TYPE_MESSAGE 数据用数据块(名为“消息”或“报文”)的形式写入管道
下述常数之一:
PIPE_READMODE_BYTE 数据以单独字节的形式从管道中读出
PIPE_READMODE_MESSAGE 数据以名为“消息”的数据块形式从管道中读出(要求指定PIPE_TYPE_MESSAGE)
下述常数之一:
PIPE_WAIT 同步操作在等待的时候挂起线程
PIPE_NOWAIT(不推荐!) 同步操作立即返回。这样可为异步传输提供一种落后的实现方法,已由Win32的重叠式传输机制取代了
nMaxInstances Long,这个管道能够创建的最大实例数量。必须是1到常数PIPE_UNLIMITED_INSTANCES间的一个值。它对于管道的所有实例来说都应是相同的
nOutBufferSize Long,建议的输出缓冲区长度;零表示用默认设置
nInBufferSize Long,建议的输入缓冲区长度;零表示用默认设置
nDefaultTimeOut Long,管道的默认等待超时。对一个管道的所有实例来说都应相同
lpSecurityAttributes SECURITY_ATTRIBUTES,指定一个SECURITY_ATTRIBUTES结构,或者传递零值(将参数声明为ByVal As Long,并传递零值),以便使用不允许继承的一个默认描述符
*/
hPipe = CreateNamedPipe(
g_szPipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFFER_SIZE,
BUFFER_SIZE,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if (INVALID_HANDLE_VALUE == hPipe){
printf("\n Error occurred while creating the pipe: %d\n",GetLastError());
return 1;
}
else{
printf("\nCreateNamedPipe() was successful.\n");
}
printf("\n Waiting for client connection...\n");
/*
BOOL WINAPI ConnectNamedPipe(
_In_ HANDLE hNamedPipe,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
ConnectNamedPipe是指示一台服务器等待下去,直至客户机同一个命名管道连接。
返回值
Long,如lpOverlapped为NULL,那么:
如管道已连接,就返回Ture(非零);如发生错误,或者管道已经连接,就返回零(GetLastError此时会返回ERROR_PIPE_CONNECTED)
lpOverlapped有效,就返回零;如管道已经连接,GetLastError会返回ERROR_PIPE_CONNECTED;如重叠操作成功完成,就返回ERROR_IO_PENDING。在这两种情况下,倘若一个客户已关闭了管道,且服务器尚未用DisconnectNamedPipe函数同客户断开连接,那么GetLastError都会返回ERROR_NO_DATA
参数表
参数 类型及说明
hNamedPipe Long,管道的句柄
lpOverlapped OVERLAPPED,如设为NULL(传递ByVal As Long),表示将线程挂起,直到一个客户同管道连接为止。否则就立即返回;此时,如管道尚未连接,客户同管道连接时就会触发lpOverlapped结构中的事件对象。随后,可用一个等待函数来监视连接
*/
BOOL bClientConnected = ConnectNamedPipe(hPipe, NULL);
if (FALSE == bClientConnected){
printf("\n Error occurred while connecting to the client:%d\n",GetLastError());
CloseHandle(hPipe);
return 1;
}
{
printf("\nConnectNamePipe() was successful...\n");
}
/*
BOOL WINAPI GetNamedPipeClientProcessId(
_In_ HANDLE Pipe,
_Out_ PULONG ClientProcessId
);
*/
//Get NamedPipeClientProcessId
PULONG ClientProcessID=0;
GetNamedPipeClientProcessId(hPipe, ClientProcessID);
if (FALSE == GetLastError()){
printf("\nCan not Require processid of NamedPipeClient...\n");
}
else{
printf("\n Current Client Process ID equal %ld\n",ClientProcessID);
}
//Get NamedPipeClient ComputerName
/*
BOOL WINAPI GetNamedPipeClientComputerName(
_In_ HANDLE Pipe,
_Out_ LPTSTR ClientComputerName,
_In_ ULONG ClientComputerNameLength
);
*/
LPTSTR ClientComputerName=NULL;
ULONG ClientComputerNameLength = 1024;
GetNamedPipeClientComputerName(hPipe, ClientComputerName, ClientComputerNameLength);
if (FALSE == GetLastError()){
printf("\nCan not require Client ComputerName of NamedPipeClient\n");
}
else{
printf("current Client Computer name of NamedPipe equal %s\n",ClientComputerName);
}
char szBuffer[BUFFER_SIZE];
DWORD cbBytes;
BOOL bResult = ReadFile(hPipe, szBuffer, sizeof(szBuffer), &cbBytes, NULL);
if ((!bResult) || (0 == cbBytes)){
printf("\n Error occurred while reading from the client:%d\n",GetLastError());
CloseHandle(hPipe);
return 1;
}
else{
printf("\n ReadFile() was successfule...\n");
}
printf("\n Client sent the following message: %s\n", szBuffer);
strcpy_s(szBuffer, ACK_MESG_RECV);
bResult = WriteFile(hPipe, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
if ((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
printf("\n Error occurred while writing to the client: %d", GetLastError());
CloseHandle(hPipe);
return 1;
}
else{
printf("\n WriteFile() was successful...\n");
}
CloseHandle(hPipe);
system("pause");
return 0;
}
- The source code is platform dependent.
- A named pipe is easy to use.
- A named pipe works across the network.
进程间通信之WinSock实现
Windows Sockets is a protocol-independent interface. It takes advantage of the communication capabilities of the underlying protocols. In Windows Sockets 2, a socket handle can optionally be used as a file handle with the standard file I/O functions.
Windows Sockets are based on the sockets first popularized by Berkeley Software Distribution (BSD). An application that uses Windows Sockets can communicate with other socket implementation on other types of systems. However, not all transport service providers support all available options.
Key Point: Windows Sockets is a protocol-independent interface capable of supporting current and emerging networking capabilities.
此程序Client和Server都必须包含ws2_32.lib
WinSock provides very high level networking capabilities. It supports TCP/IP (the most widely used protocol), along with many other protocols – AppleTalk, DECNet, IPX/SPX etc.
WinSock supports Berkeley sockets, along with many other Windows specific extensions.
Following are some of the WinSock calls:
- socket()
- bind()
- listen()
- accept()
- connect()
- send()
- recv()
Source Code of Server:
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define ACK_MESG_RECV "Message received successfully"
int main(int argc, char* argv[], char* env[]){
WSADATA wsaData;
int nResult;
nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (NO_ERROR != nResult){
printf("\n Error occurred while executing WSAStartup()...");
return 1;
}
else{
printf("\nWSAStartup() was successful...\n");
}
SOCKET listensocket, remotesocket;
int port_no, client_length;
char szBuffer[256];
struct sockaddr_in ServerAddress, ClientAddress;
listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == listensocket){
printf("\nError occurred while opening socket: %ld.\n",WSAGetLastError());
WSACleanup();
return 0;
}
else{
printf("\n socket() was successful...\n");
}
ZeroMemory((char*)&ServerAddress, sizeof(ServerAddress));
port_no = atoi("4455");
ServerAddress.sin_family = AF_INET;
ServerAddress.sin_addr.S_un.S_addr = INADDR_ANY;
ServerAddress.sin_port = htons(port_no);
if (SOCKET_ERROR == bind(listensocket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress))){
closesocket(listensocket);
printf("\nError occurred while binding...\n");
WSACleanup();
return 0;
}
else{
printf("bind() was successful..\n");
}
if (SOCKET_ERROR == listen(listensocket, SOMAXCONN)){
closesocket(listensocket);
printf("\n Error occurred while listening...\n");
WSACleanup();
return 0;
}
else{
printf("\nlisten() was successful...\n");
}
client_length = sizeof(ClientAddress);
remotesocket = accept(listensocket, (struct sockaddr *)&ClientAddress, &client_length);
if (INVALID_SOCKET == remotesocket){
closesocket(listensocket);
printf("\nError occurred while accepting socket:%ld..\n", WSAGetLastError());
WSACleanup();
return 0;
}
else{
printf("\naccept() was successful...\n");
}
printf("\n Client connected from :%s\n", inet_ntoa(ClientAddress.sin_addr));
ZeroMemory(szBuffer, 256);
int nBytesSent;
int nBytesRecv;
nBytesRecv = recv(remotesocket, szBuffer, 255, 0);
if (SOCKET_ERROR == nBytesRecv){
closesocket(listensocket);
closesocket(remotesocket);
printf("\nError occurred while receiving from socket...\n");
WSACleanup();
return 1;
}
else{
printf("\nrecv() was successful...\n");
}
printf("\nThe following message was received:%s\n", szBuffer);
nBytesSent = send(remotesocket, ACK_MESG_RECV, strlen(ACK_MESG_RECV), 0);
if (SOCKET_ERROR == nBytesSent){
closesocket(listensocket);
closesocket(remotesocket);
printf("\nError occurred while writing to socket.\n");
WSACleanup();
return 0;
}
else{
printf("\nsend() was successful...\n");
}
closesocket(listensocket);
closesocket(remotesocket);
WSACleanup();
system("pause");
return 0;
}
Source Code of Client:
//#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib, "ws2_32.lib")
/*
*/
int main(int argc, char* argv[], char* env[]){
WSADATA wsaData;
/*
WSAStartup,是Windows Sockets Asynchronous的启动命令、Windows下的网络编程接口软件 Winsock1 或 Winsock2 里面的一个命令。
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
⑴ wVersionRequested:一个WORD(双字节)型数值,在最高版本的Windows Sockets支持调用者使用,高阶字节指定小版本(修订本)号,低位字节指定主版本号。
⑵lpWSAData 指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
WindowsSockets API提供的调用方可使用的最高版本号。高位字节指出副版本(修正)号,低位字节指明主版本号。
本函数必须是应用程序或DLL调用的第一个Windows Sockets函数。它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节。应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数。
返回值
0 成功。
否则返回下列的错误代码之一。注意通常依靠应用程序调用WSAGetLastError()机制获得的错误代码是不能使用的,因为Windows Sockets DLL可能没有建立“上一错误”信息储存的客户数据区域。
关于Windows Sockets提供者的说明:
每一个Windows Sockets应用程序必须在进行其它Windows Sockets API调用前进行WSAStartup()调用。这样,本函数就可以用于初始化的目的。
*/
int nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (NO_ERROR != nResult){
printf("\n Error occurred while executing WSAStartup()...");
return 1;
}
else{
printf("\nWSAStartup() successful...");
}
//客户端的socket对象结构
SOCKET clientsocket;
//连接服务器的端口
int port_no;
//服务器的socket信息
struct sockaddr_in ServerAddress;
//struct hostent *Server;
//输入输出缓冲区大小
char szBuffer[256];
//char* to int
port_no = atoi("4455");
/*
socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。如果协议protocol未指定(等于0),则使用缺省的连接方式。
对于使用一给定地址族的某一特定套接口,只支持一种协议。但地址族可设为AF_UNSPEC(未指定),这样的话协议参数就要指定了。协议号特定于进行通讯的“通讯域”。
int socket( int af, int type, int protocol);
af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。
type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。
protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。
若无错误发生,socket()返回引用新套接口的描述字。否则的话,返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()获取相应错误代码。
*/
clientsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == clientsocket){
printf("\n Error occurred while opening socket: %d\n", WSAGetLastError());
WSACleanup();
return 0;
}
else{
printf("\n socket() successful.\n");
}
//int ret = getaddrinfo("127.0.0.1", "4455",)
/*Server = gethostbyname("127.0.0.1");
if (Server == NULL){
closesocket(clientsocket);
printf("\nError occurred no such host\n");
WSACleanup();
return 0;
}
else{
printf("\nGethostbyname() was successful\n");
}*/
//清理缓冲区
ZeroMemory((char*)&ServerAddress,sizeof(ServerAddress));
/*
socket_in成员属性
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序),在linux下,端口号的范围0~65535,同时0~1024范围的端口号已经被系统使用或保留。
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
*/
ServerAddress.sin_family = AF_INET;
ServerAddress.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ServerAddress.sin_port = htons(port_no);
//CopyMemory((char*)&ServerAddress.sin_addr.S_un.S_addr, (char*)Server->h_addr_list, Server->h_length);
//ServerAddress.sin_port = htons(port_no);
//if (SOCKET_ERROR == connect(clientsocket, reinterpret_cast<const struct sockaddr *>(&ServerAddress), sizeof(ServerAddress))){
/*
函数原型: int connect(int s, const struct sockaddr * name, int namelen);
参数:
s:标识一个未连接socket
name:指向要连接套接字的sockaddr结构体的指针
namelen:sockaddr结构体的字节长度
若无错误发生,则connect()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
*/
if (SOCKET_ERROR == connect(clientsocket, (SOCKADDR*)&ServerAddress, sizeof(SOCKADDR))){
closesocket(clientsocket);
printf("\nError occurred while connecting...");
WSACleanup();
return 1;
}
else{
printf("\nconnect() successful...");
}
ZeroMemory(szBuffer, 256);
printf("\nPlease enter message to be sent to server..\n");
fflush(stdin);
fgets(szBuffer, 256, stdin);
int nBytesSent; //发送的字节大小
int nBytesRecv; //接受的字节大小
/*
send()用于向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR。[1] send也是一个英文单词。
向一个已连接的套接口发送数据。
#include <winsock.h>
int PASCAL FAR send( SOCKET s, const char FAR* buf, int len, int flags);
s:一个用于标识已连接套接口的描述字。
buf:包含待发送数据的缓冲区。
len:缓冲区中数据的长度。
flags:调用执行方式。
若无错误发生,send()返回所发送数据的总数(请注意这个数字可能小于len中所规定的大小)。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
*/
nBytesSent = send(clientsocket, szBuffer, strlen(szBuffer), 0);
if (SOCKET_ERROR == nBytesSent){
closesocket(clientsocket);
printf("\nError occurred while writing to socket...");
WSACleanup();
return 0;
}
else{
printf("\nsend() was successful...");
}
ZeroMemory(szBuffer, 256);
/*
函数原型int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
返回值:
若无错误发生,recv()返回读入的字节数。如果连接已中止,返回0。如果发生错误,返回-1,应用程序可通过perror()获取相应错误信息。
*/
nBytesRecv = recv(clientsocket, szBuffer, 255, 0);
if (SOCKET_ERROR == nBytesRecv){
closesocket(clientsocket);
printf("\n Error occurred while reading from socket\n");
WSACleanup();
return 0;
}
else{
printf("\nrecv() was successful...\n");
}
printf("\n%s\n", szBuffer);
/*
本函数关闭一个套接口。更确切地说,它释放套接口描述字s,以后对s的访问均以WSAENOTSOCK错误返回。若本次为对套接口的最后一次访问,则相应的名字信息及数据队列都将被释放。
关闭一个套接口。
#include <winsock.h>
int PASCAL FAR closesocket( SOCKET s);
s:一个套接口的描述字。
返回值:
如无错误发生,则closesocket()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
*/
closesocket(clientsocket);
/*
WSACleanup()的功能是 终止Winsock 2 DLL (Ws2_32.dll) 的使用.
头文件
#include <Winsock2.h>
引用库
#pragma comment(lib, "ws2_32.lib")
静态加入一个lib文件也就是库文件ws2_32.lib文件,提供相关API的支持,否则需要动态载入ws2_32.dll。
函数原型
int PASCAL FAR WSACleanup (void);
返回值
操作成功返回值为0;否则返回值为SOCKET_ERROR,可以通过调用WSAGetLastError获取错误代码。
在一个多线程的环境下,WSACleanup()中止了Windows Sockets在所有线程上的操作.
*/
WSACleanup();
system("pause");
return 0;
}
Widely used, and works on the same computer as well as across networks. Moreover, it can be used across various platforms and protocols.
Using WinSock requires a knowledge of relatively advanced networking concepts.
进程间通信之邮路实现
Mailslots provide one-way communication. Any process that creates a mailslot is a mailslot server. Other processes, called mailslot clients, send messages to the mailslot server by writing a message to its mailslot. Incoming messages are always appended to the mailslot. The mailslot saves the messages until the mailslot server has read them. A process can be both a mailslot server and a mailslot client, so two-way communication is possible using multiple mailslots.
A mailslot client can send a message to a mailslot on its local computer, to a mailslot on another computer, or to all mailslots with the same name on all computers in a specified network domain. Messages broadcast to all mailslots on a domain can be no longer than 400 bytes, whereas messages sent to a single mailslot are limited only by the maximum message size specified by the mailslot server when it created the mailslot.
Key Point: Mailslots offer an easy way for applications to send and receive short messages. They also provide the ability to broadcast messages across all computers in a network domain.
Mailslot is used for one way inter-process communications. There is a Mailslot server which will be read-only; it will just read the client sent messages. The clients will be write-only clients, sending messages to the server. Mailslot messages can be of around 400 bytes only.
Mailslot can broadcast messages in a domain. If processes in a domain create a mailslot with the same name, then a message that is sent to that mailslot is sent to all of these processes.
Following are some of the Win32 APIs that are used when working with Mailslot:
- CreateMailSlot()
- GetMailslotInfo()
- SetMailslotInfo()
- ReadFile()
- WriteFile()
- CloseHandle()
A Mailslot name needs to be in the following format:
- \ComputerName\mailslot[path]name
- \DomainName\mailslot[path]name
- \*\mailslot[path]name
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
TCHAR g_szMailSlot[] = _T("\\\\.\\mailslot\\MyMailSlot");
#define MAX_BUFFER_SIZE 1024
int main(int argc, char* argv[], char* env[]){
HANDLE hMailSlot;
hMailSlot = CreateFile(g_szMailSlot, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hMailSlot){
printf("\nError occurred while executing CreateFile()...%ld\n", GetLastError());
return 1;
}
else{
printf("\nCreateFile() was successfule...\n");
}
char szBuffer[MAX_BUFFER_SIZE];
DWORD cbBytes;
printf("\nEnter a message to be sent to the server:");
fflush(stdin);
fgets(szBuffer, MAX_BUFFER_SIZE - 1, stdin);
BOOL bResult = WriteFile(hMailSlot, szBuffer, strlen(szBuffer) + 1, &cbBytes, NULL);
if((!bResult) || (strlen(szBuffer) + 1 != cbBytes)){
printf("\nError occurred while writing to the server:%ld\n", GetLastError());
CloseHandle(hMailSlot);
return 1;
}
else{
printf("\nWriteFile() was successful...\n");
}
CloseHandle(hMailSlot);
system("pause");
return 0;
}
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <tchar.h>
TCHAR g_szMailslot[] = _T("\\\\.\\mailslot\\MyMailSlot");
#define MAX_BUFFER_SIZE 1024
int main(int argc, char* argv[], char* env[]){
HANDLE hMailSlot;
/*
HANDLE WINAPI CreateMailslot(
_In_ LPCTSTR lpName, //指定邮路的名字,采用的形式如下:\\.\邮路\[路径\]邮路名
_In_ DWORD nMaxMessageSize, //指定一个邮路消息的最大长度。零表示无限长。请注意,对于穿越一个网络域到多个邮路的广播消息,最大长度是400
_In_ DWORD lReadTimeout, //等待指定的数据时,用这个参数指定邮路使用的默认超时设置,以毫秒为单位。零表示不等待。常数MAILSLOT_WAIT_FOREVER表示一直等到数据到达
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes //指定一个结构,或传递零值(将参数声明为ByVal As Long,并传递零值),表示使用不允许继承的默认描述符
);
*/
hMailSlot = CreateMailslot(g_szMailslot, MAX_BUFFER_SIZE, MAILSLOT_WAIT_FOREVER, NULL);
if (INVALID_HANDLE_VALUE == hMailSlot){
printf("\nError occurred while executing CreateMailslot()...\n");
return 1;
}
else{
printf("\nCreateMailslot() was successful...\n");
}
char szBuffer[MAX_BUFFER_SIZE];
DWORD cbBytes;
BOOL bResult;
printf("\nWaiting for client connection...");
while (TRUE){
bResult = ReadFile(hMailSlot, szBuffer, MAX_BUFFER_SIZE - 1, &cbBytes, NULL);
if ((!bResult) || (0 == cbBytes)){
printf("\nError occurred while executing readfile() from the client:%d...\n",GetLastError());
CloseHandle(hMailSlot);
return 1;
}
else{
printf("\nReadFile() was successful...\n");
}
printf("\nClient sent thefollowing message:%s\n", szBuffer);
}
CloseHandle(hMailSlot);
system("pause");
return 0;
}
- Source code is platform dependent.
- Mailslot provides one-way communication only.
- Size of message is limited to around 400 bytes.
- Mailslot supports broadcasting.
- Works across a network.
- Mailslot is easy to use.
Threads vs. processes
Threads differ from traditional multitasking operating system processes in that:
- processes are typically independent, while threads exist as subsets of a process
- processes carry considerably more state information than threads, whereas multiple threads within a process share process state as well as memory and other resources
- processes have separate address spaces, whereas threads share their address space
- processes interact only through system-provided inter-process communication mechanisms
- context switching between threads in the same process is typically faster than context switching between processes.
线程间通信
线程基本概念
In computer science, a thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system. The implementation of threads and processes differs between operating systems, but in most cases a thread is a component of a process. Multiple threads can exist within one process, executing concurrently and sharing resources such as memory, while different processes do not share these resources. In particular, the threads of a process share its executable code and the values of its variables at any given time.
Systems with a single processor generally implement multithreading by time slicing: the central processing unit (CPU) switches between different software threads. This context switching generally happens very often and rapidly enough that users perceive the threads or tasks as running in parallel. On a multiprocessor or multi-core system, multiple threads can execute in parallel, with every processor or core executing a separate thread simultaneously; on a processor or core with hardware threads, separate software threads can also be executed concurrently by separate hardware threads.
Single threading
In computer programming, single threading is the processing of one command at a time. The opposite of single threading is multithreading. While it has been suggested that the term single threading is misleading, the term has been widely accepted within the functional programming community.
Multithreading
Multithreading is mainly found in multitasking operating systems. Multithreading is a widespread programming and execution model that allows multiple threads to exist within the context of one process. These threads share the process’s resources, but are able to execute independently. The threaded programming model provides developers with a useful abstraction of concurrent execution. Multithreading can also be applied to one process to enable parallel execution on a multiprocessing system.
线程通信方式
使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,下面我们只介绍最常用的四种:
- 临界区(CCriticalSection)
- 事件(CEvent)
- 互斥量(CMutex)
- 信号量(CSemaphore)
Reference
- gcc4.7编译thread程序
- pin 从windows 到 linux
- Pin工具的介绍
- C++11 多线程
- C++多线程编程(四)线程的同步
- C++多线程编程(1)C++组件
- Inter-process communication
- Inter-thread communication in java
- Thread (computing)
- Multithreading_(computer_architecture)
- Communicating sequential processes
- Interprocess Communications
- Windows IPC
- Synchronization of multiple reader and writer processes using Win32 APIs
- Basic Guide of Interprocess Communication and Pipes
- 多进程与多线程(三)–多进程 or 多线程
- Windows 多进程通信API总结
- fget(str,n,fd)和 stdin stdout
- C语言文件操作之fgets()
- fgets
- LPTSTR
- WaitForMultipleObjects
- WaitForSingleObject
- MapViewOfFile
- CreateEvent
- sprintf
- Linux多线程编程实例
- Creating Named Shared Memory
- CreateFile
- CreateNamedPipe
- CreateNamedPipe function
- ConnectNamedPipe
- GetNamedPipeClientComputerName function
- 简单的 C++ SOCKET编程 —基于TCP/IP协议(转)
- WSAStartup
- socket()
- SOCKADDR_IN
- connect()
- send()
- recv()
- WSACleanup()
- closesocket()
- CreateMailslot function
- Interprocess Communications