Windows网络编程笔记3 ---- 邮槽和命名管道
邮槽和命名管道的使用方法也很简单,只需几个有限的函数就可以实现双方的通信。
第三、邮槽
邮槽----进程间通信机制。
通过邮槽客户进程可以将消息通过广播给一个或多个服务进程。这是一个单向通信机制,缺点是只允许从客户机到服务器,优点也是这个原理,使客户机应用能够非常容易地将广播消息发送给一个或多个服务器应用。邮槽是一种无连接方式,是一种”不可靠“的数据传输。
邮槽名也使用UNC路径,第二个关键字是Mailslot,不可改变
\\\\server\\Mailslot\\[path]name
服务器实现过程:
CreateMailslot();//创建一个邮槽句柄
ReadFile();//接受任何客户机的数据
CloseHandle();//关闭邮槽句柄
服务器端邮槽实现
//server1.cpp
//邮槽的实现
#include "windows.h"
#include "winbase.h"
#include "stdio.h" void main()
{
HANDLE Mailslot;
char buffer[];
DWORD NumberOfBytesRead; //创建邮槽
if ((Mailslot = CreateMailslot("\\\\.\\Mailslot\\Myslot",,MAILSLOT_WAIT_FOREVER,NULL))
== INVALID_HANDLE_VALUE)
{
printf("Failed to create a mailslot %d\n",GetLastError());
getchar();
return ;
} //从邮槽中读取数据,只有服务器才能从邮槽中读取数据
while(ReadFile(Mailslot,buffer,,&NumberOfBytesRead,NULL) != )
{
printf("%.*s\n",NumberOfBytesRead,buffer);
}
if (!Mailslot)
{
CloseHandle(Mailslot);
}
}
客户端实现过程
CreateFile();//打开指向邮槽的句柄
WriteFile();//写入数据
CloseHandle();//关闭句柄
客户端邮槽代码实现
//client.cpp
//邮槽客户端 #include "windows.h"
#include "stdio.h" void main()
{
HANDLE Mailslot;
DWORD ByteWritten;
CHAR ServerName[]; if((Mailslot=CreateFile("\\\\.\\Mailslot\\Myslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIB UTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE)//这里就是邮槽的创建
{
printf("Createfile failed with error %d\n",GetLastError());
getchar();
return ;
} if (WriteFile(Mailslot,"This is a test",,&ByteWritten,NULL) == )//写入数据
{
printf("WriteFile failed with error %d\n",GetLastError());
getchar();
return ;
} printf("Write %d bytes\n",ByteWritten);
getchar();
CloseHandle(Mailslot);
}
下面在服务器上添加线程,用于自己终止程序运行,而不是让其一直处于挂起状态。这种方式是为了避免服务器在ReadFile()时服务器程序因某种原因而终止,此时ReadFile()没有完成,程序会一直处于挂起状态,直到有数据可读。
服务器端可以自己终止接受数据
//server2.cpp
//邮槽的线程实现
#include "windows.h"
#include "winbase.h"
#include "stdio.h"
#include "conio.h" BOOL StopProcessing; DWORD WINAPI ServerMailslot(LPVOID lpParameter);
void SendMessageToMailslot(void);//自己向邮槽发送数据 void main()
{
HANDLE MailslotThread;
DWORD ThreadID;
StopProcessing = FALSE;
MailslotThread = CreateThread(NULL,,ServerMailslot,NULL,,&ThreadID); printf("Press a key to stop the server\n");
_getch(); //按下按键以后,赋值为TRUE,在线程中终止程序运行
StopProcessing = TRUE; //发送消息,之后线程会进入while()循环,并且会终止循环
SendMessageToMailslot();//自己向邮槽发送数据 //
if (WaitForSingleObject(MailslotThread,INFINITE) == WAIT_FAILED)
{
printf("WaitForSingleObject Failed with error %d\n",GetLastError());
getchar();
return ;
}
} //线程入口函数
DWORD WINAPI ServerMailslot(LPVOID lpParameter)
{
char buffer[];
DWORD NumberOfBytesRead;
DWORD Ret;
HANDLE Mailslot; if ((Mailslot = CreateMailslot("\\\\.\\mailslot\\myslot",,MAILSLOT_WAIT_FOREVER,NULL))
== INVALID_HANDLE_VALUE)
{
printf("Failed to create a mailslot %d\n",GetLastError());
getchar();
return ;
} while ((Ret = ReadFile(Mailslot,buffer,,&NumberOfBytesRead,NULL)) != )
{
if(StopProcessing)
break;
printf("Receive %d bytes \n",NumberOfBytesRead);
}
CloseHandle(Mailslot);
return ;
} //发送终止信息
void SendMessageToMailslot()
{
HANDLE Mailslot;
DWORD BytesWritten;
if ((Mailslot = CreateFile("\\\\.\\mailslot\\myslot",GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING
,FILE_ATTRIBUTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateFile Failed with error %d\n",GetLastError());
getchar();
return ;
}
if (WriteFile(Mailslot,"STOP",,&BytesWritten,NULL) == )
{
printf("WriteFile Failed with error %d\n",GetLastError());
getchar();
return ;
}
CloseHandle(Mailslot);
}
这样可以避免服务器进入无限的等待。
邮槽总结:
使用邮槽,应用程序可以在Windows重定向器的帮助下,实现简单的单向进程间数据通信。对邮槽来说,它最有价值的一项功能便是通过网络,将一条消息广播给一台或多台计算机。然而,邮槽并未提供对数据可靠传输的保障。是一种不可靠的数据传输。
第四、命名管道
命名管道实际上建立一个简单的客户机/服务器数据通信体系,可在其中可靠地传输数据。
规则:
命名管道的标识是采用 U N C格式进行的:\ \ server\pipe\ [ path ]name
其中的pipe是一个标记,不能改变,不区分大小写。例子如下:
\\\\ myserver\\pipe\\mypipe
通信方式
命名管道提供了两种基本通信模式:字节模式和消息模式。
在字节模式中,消息以一个连续的字节流的形式,在客户机与服务器之间流动。在一方写入某个数量的字节,并不表示在另一方会读出等量的字节。这样一来,客户机和服务器在传输数据的时候,便不必关心数据的内容。
在消息模式中,客户机和服务器则通过一系列不连续的数据单位,进行数据的收发。每次在管道上发出了一条消息后,它必须作为一条完整的消息读入。
服务器与客户机的区别
命名管道的最大特点就是建立了一个基于服务器/客户机的程序设计体系。在这个体系结构中,数据既可以单向流动,也可以双向流动。但是服务器是唯一一个有权利创建命名管道的进程,也只有它有权利接受来自客户端的链接请求。
服务器的实现过程
CreateNamedPipe();//创建命名管道实例句柄
ConnectNamedPipe();//监听来自客户机的链接请求
ReadFile(),WriteFile();//读写数据
DisconnectNmaePipe();//关闭命名通道连接
CloseHandle();//关闭命名管道实例句柄
客户端实现过程
WaitNamedPipe();// 等候一个命名管道实例可供自己使用
CreateFile();// 建立与命名管道的连接
WriteFile(); ,ReadFile();//读写数据
CloseHandle();// 关闭命名管道会话
简单命名管道的实现
//命名管道的实现 #include "windows.h"
#include "stdio.h" void main()
{
HANDLE PipeHnadle;
DWORD BytesRead;
CHAR buffer[]; if ((PipeHnadle = CreateNamedPipe("\\\\.\\Pipe\\Song",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE
,,,,,NULL)) == INVALID_HANDLE_VALUE)//创建
{
printf("CreateNamedPipe failed with error %d\n",GetLastError());
getchar();
return ;
}
printf("Server is now running!\n"); if (ConnectNamedPipe(PipeHnadle,NULL) == )//连接客户端
{
printf("ConnectNamedPipe failed with error %d\n",GetLastError());
CloseHandle(PipeHnadle);
getchar();
return ;
} if (ReadFile(PipeHnadle,buffer,sizeof(buffer),&BytesRead,NULL) <= )//读取数据
{
printf("ReadFile failed with error %d\n",GetLastError());
CloseHandle(PipeHnadle);
getchar();
return ;
} printf("%.*s\n",BytesRead,buffer); if (DisconnectNamedPipe(PipeHnadle) == )//关闭
{
printf("DisconnectNamedPipe failed with error %d\n",GetLastError());
getchar();
return ;
} CloseHandle(PipeHnadle);
getchar();
getchar();
}
同时控制多个管道实例,命名管道可以实现多个实例的连接。这个数量有函数CreateNamedPipe()控制
HANDLE WINAPI CreateNamedPipe(
_In_ LPCTSTR lpName,//管道名称
_In_ DWORD dwOpenMode,//打开模式,如PIPE_ACCESS_DUPLES 双向管道,PIPE_FLAG_OVEERLAPPED(重叠I/O)
_In_ DWORD dwPipeMode,//管道模式,字节流还是信息流
_In_ DWORD nMaxInstances,//最大连接实例数量
_In_ DWORD nOutBufferSize,//输出缓冲区大小
_In_ DWORD nInBufferSize,//输入缓冲区大小
_In_ DWORD nDefaultTimeOut,//超时设置
_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes//安全描述符
);
多管道实现,使用线程
//命名管道的实现,多线程 #include "windows.h"
#include "stdio.h"
#include "conio.h" #define NUM_PIPES 5 DWORD WINAPI PipeInstanceProc(LPVOID lpParameter);
void main()
{
HANDLE ThreadHandle;
INT i;
DWORD ThreadID; for(i = ; i < NUM_PIPES ;i ++ )
{
//创建线程保存管道实例
if ((ThreadHandle = CreateThread(NULL,,PipeInstanceProc,
NULL,,&ThreadID)) == NULL)
{
printf("CreateThread failed with error %d \n",GetLastError());
getchar();
return ;
}
CloseHandle(ThreadHandle);
} printf("Press any key to stop the server!\n");
_getch();
} //入口函数 DWORD WINAPI PipeInstanceProc(LPVOID lpParameter)
{
HANDLE PipeHandle;
DWORD BytesRead;
DWORD BytesWritten;
CHAR buffer[]; if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\Song",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE | PIPE_READMODE_BYTE
,NUM_PIPES,,,,NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe failed with error %d \n",GetLastError());
getchar();
return ;
}
//一直尝试连接客户端
while (true)
{
if (ConnectNamedPipe(PipeHandle,NULL) == )
{
printf("ConnectNamedPipe failed with error %d \n",GetLastError());
getchar();
break;
}
//读取数据
while (ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) > )
{
printf("Echo %d bytes to client \n",BytesRead);
if (WriteFile(PipeHandle,buffer,BytesRead,&BytesWritten,NULL) == )
{
printf("WriteFile failed with error %d \n",GetLastError());
getchar();
break;
}
}
if (DisconnectNamedPipe(PipeHandle) == )
{
printf("DisconnectNamedPipe failed with error %d \n",GetLastError());
getchar();
break;
}
}
CloseHandle(PipeHandle);
return ;
}
最后看一下基于重叠I/O模式的管道通信
这个重叠I/O的设置在CreateNamedPipe();的第二个参数 dwOpenMode,只需在里面包含 PIPE_FLAG_OVEERLAPPED就行。
//overlapped_server.cpp //重叠I/O方式是下命名管道 #include "windows.h"
#include "stdio.h" #define NUM_PIPES 5
#define BUFFER_SIZE 256 void main()
{
HANDLE PipeHandles[NUM_PIPES];
DWORD BytesTransferred;
CHAR buffer[NUM_PIPES][BUFFER_SIZE];
int i;
OVERLAPPED ovlap[NUM_PIPES];
HANDLE Event[NUM_PIPES]; ////////////////////////////////////////////////////////////////////////// BOOL DataRead[NUM_PIPES];
DWORD Ret;
DWORD Pipe; for (i = ; i < NUM_PIPES ; i ++)
{
//创建命名管道实例
if ((PipeHandles[i] = CreateNamedPipe("\\\\.\\pipe\\Song",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,NUM_PIPES,,,,NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateNamedPipe for pipe %d failed with error %d\n",i,GetLastError());
getchar();
return ;
}
//创建事件
if ((Event[i] = CreateEvent(NULL,TRUE,FALSE,NULL)) == NULL)
{
printf("CreateEvent for pipe %d failed with error %d\n",i,GetLastError());
getchar();
continue ;
}
//保存事件状态
DataRead[i] = FALSE;
ZeroMemory(&ovlap[i],sizeof(OVERLAPPED)); //监听事件
if (ConnectNamedPipe(PipeHandles[i],&ovlap[i]) == )
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("ConnectNamedPipe for pipe %d failed with error %d\n",i,GetLastError());
CloseHandle(PipeHandles[i]);
getchar();
return ;
}
}
} //
printf("Server is running!\n");
////////////////////////////////////////////////////////////////////////// //读取数据
while (true)
{
if ((Ret = WaitForMultipleObjects(NUM_PIPES,Event,FALSE,INFINITE)) == WAIT_FAILED)
{
printf("WaitForMultipleObjects failed with error %d\n",GetLastError());
getchar();
return ;
}
Pipe = Ret - WAIT_OBJECT_0;
ResetEvent(Event[Pipe]); //检查I/O状态,如果失败,就断开连接并重新尝试读取数据
if (GetOverlappedResult(PipeHandles[Pipe],&ovlap[Pipe],&BytesTransferred,TRUE) == )
{
printf("GetOverlappedResult failed with error %d\n",GetLastError());
//断开连接
if (DisconnectNamedPipe(PipeHandles[Pipe]) == )
{
printf("DisconnectNamedPipe failed with error %d\n",GetLastError());
return ;
}
if (ConnectNamedPipe(PipeHandles[Pipe],&ovlap[pipe]) == )
{
if (GetLastError() != ERROR_IO_PENDING)
{
//服务器出错,关闭句柄
printf("ConnectNamedPipe for pipe %d failed with error %d\n",i,GetLastError());
CloseHandle(PipeHandles[Pipe]);
}
} DataRead[Pipe] = FALSE;
}
else
{
//如果管道上有数据就读取并发送客户端,如果没有就一直尝试读取
if (DataRead[Pipe] == FALSE)
{
ZeroMemory(&ovlap[Pipe],sizeof(OVERLAPPED));
ovlap[Pipe].hEvent = Event[Pipe]; if (ReadFile(PipeHandles[Pipe],buffer[Pipe],BUFFER_SIZE,NULL,&ovlap[Pipe]) == )
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("ReadFile failed with error %d\n",GetLastError()); }
}
DataRead[Pipe] = TRUE;
}
else
{
//向管道写入数据
printf("Received %d bytes ,echo bytes back \n",BytesTransferred);
ZeroMemory(&ovlap[Pipe],sizeof(OVERLAPPED));
ovlap[Pipe].hEvent = Event[Pipe]; if (WriteFile(PipeHandles[Pipe],buffer[Pipe],BUFFER_SIZE,NULL,&ovlap[Pipe]) == )
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("WriteFile failed with error %d\n",GetLastError()); }
}
DataRead[Pipe] = FALSE;
} }
}
}
再看一下客户端的实现
//client3.cpp //简单命名管道客户机 #include "windows.h"
#include "stdio.h"
#include "conio.h" #define PIPE_NAME "\\\\.\\pipe\\Song" void main()
{ HANDLE PipeHandle;
DWORD BytesWritten;
DWORD BytesRead;
CHAR buffer[];
memset(&buffer,,sizeof(buffer));
//等待可用的命名管道
if (WaitNamedPipe(PIPE_NAME,NMPWAIT_WAIT_FOREVER) == )
{
printf("WaitNamedPipe failed with error %d \n",GetLastError());
getchar();
return ;
}
//创建命名管道句柄 if ((PipeHandle = CreateFile(PIPE_NAME,GENERIC_READ | GENERIC_WRITE,
,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with error %d \n",GetLastError());
getchar();
return ;
}
//写入数据
if ((WriteFile(PipeHandle,"This is a test",,&BytesWritten,NULL)) == )
{
printf("WriteFile failed with error %d \n",GetLastError());
CloseHandle(PipeHandle);
getchar();
return ;
} printf("Wrote %d bytes\n",BytesWritten); //读取数据
if (ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) > )
{
printf("Receive:dfj %s\n",buffer);
printf("Read %d bytes\n",BytesWritten);
getchar();
return ;
} getchar();
CloseHandle(PipeHandle); }
最后还有几个简化后的API,使用起来更加方便
CallNamedPipe();//客户机,可同时读写数据
TransactNamedPipe();//客户机、服务器,可同时读写数据
BOOL WINAPI CallNamedPipe(
_In_ LPCTSTR lpNamedPipeName,//管道名称,采用UNC格式
_In_ LPVOID lpInBuffer,//发送数据缓存区
_In_ DWORD nInBufferSize,//数据缓存区大小
_Out_ LPVOID lpOutBuffer,//接受数据缓存区
_In_ DWORD nOutBufferSize,//接受数据缓存区大小
_Out_ LPDWORD lpBytesRead,//从管道中读取的数据大小
_In_ DWORD nTimeOut//超时
);
其中nTimeOut 的参数可供选择为:
NMPWAIT_NOWAIT //不可用则直接退出,并返回错误
WMPWAIT_WAIT_FOREVER//一直等待
NMPWAIT_USE_DEFAULT_WAIT//使用默认的超时设置
BOOL WINAPI TransactNamedPipe(
_In_ HANDLE hNamedPipe,//命名管道句柄
_In_ LPVOID lpInBuffer,//发送数据缓冲区
_In_ DWORD nInBufferSize,//缓冲区大小
_Out_ LPVOID lpOutBuffer,//接受数据缓冲区
_In_ DWORD nOutBufferSize,//缓冲区大小
_Out_ LPDWORD lpBytesRead,//实际读取的数据多少
_Inout_opt_ LPOVERLAPPED lpOverlapped//重叠I/O
);
GetNamedPipeHandleState();//
用于接收与一个指定命名管道对应的信息,比如运行模式(消息或字节模式)、管道实例数以及缓冲区信息等等。
BOOL WINAPI GetNamedPipeHandleState(
_In_ HANDLE hNamedPipe,//命名管道句柄
_Out_opt_ LPDWORD lpState,//管道状态PIPE_NOWAIT,PIPE_READMODE_MESSAGE
_Out_opt_ LPDWORD lpCurInstances,//当前管道的实例数量
_Out_opt_ LPDWORD lpMaxCollectionCount,//实际最大字节数
_Out_opt_ LPDWORD lpCollectDataTimeout,//超时
_Out_opt_ LPTSTR lpUserName,//客户机名称
_In_ DWORD nMaxUserNameSize//客户机数量
);
SetNamedPipeHandleState ();//设置管道的一些参数,传输模式
BOOL WINAPI SetNamedPipeHandleState(
_In_ HANDLE hNamedPipe,//命名管道句柄
_In_opt_ LPDWORD lpMode,//传输模式,字节流或者信息流
_In_opt_ LPDWORD lpMaxCollectionCount,//最大字节数
_In_opt_ LPDWORD lpCollectDataTimeout//超时
);
lpMode的参数有两个PIPE_READMODE_BYTE(字节流),PIPE_READMODE_MESSAGE(消息流)
GetNamedPipeInfo();//获得缓冲区大小以及管道实例最大数量信息
BOOL WINAPI GetNamedPipeInfo(
_In_ HANDLE hNamedPipe,
_Out_opt_ LPDWORD lpFlags,//管道类型,服务器或者客户端
_Out_opt_ LPDWORD lpOutBufferSize,
_Out_opt_ LPDWORD lpInBufferSize,
_Out_opt_ LPDWORD lpMaxInstances//管道最大实例数量
);
lpFlags的参数选择为:
PIPE_CLIENT_END//客户机
PIPE_SERVER_END//服务器
PIPE_TYPE_BYTE//字节流
PIPE_TYPE_MESSAGE//消息流
PeekNamedPipe();//可用它对命令管道内的数据进行浏览,同时毋需将其从管道的内部缓冲区挪出。
BOOL WINAPI PeekNamedPipe(
_In_ HANDLE hNamedPipe,
_Out_opt_ LPVOID lpBuffer,//读取数据缓冲区
_In_ DWORD nBufferSize,//缓冲区大小
_Out_opt_ LPDWORD lpBytesRead,//读取数据大小
_Out_opt_ LPDWORD lpTotalBytesAvail,//接收可从管道发出的字节总数
_Out_opt_ LPDWORD lpBytesLeftThisMessage//于接收消息内尚存的字节数量(前提是管道用消息模式打开
);
Windows网络编程笔记3 ---- 邮槽和命名管道的更多相关文章
- Windows网络编程笔记1
第一部分 传统网络API 传统的网络接口NetBIOS.重定向器.邮槽.命名管道等.第一,NetBIOS(Network Basic Input/Output System, NetBIOS)“网络基 ...
- Windows网络编程笔记4 -- Winsock 协议相关知识
Win32平台上的Winsock编程,Winsock是一个与协议无关的接口.以下协议是我们需要了解的: 网络协议的特征包括: 1. 面向消息 2. 面向连接和无线接 3. 可靠性和次序性 4. ...
- Windows网络编程笔记6 --- WinSock I/O 控制方法
Windows提供了两种方式“套接字模式”和“套接字I/O模型”,可对一个套接字上的I/O行为加以控制.套接字模式用于决定在随一个套接字调用时,那些 Winsock函数的行为.其中的模型包括括sele ...
- Windows网络编程笔记5 -- 其他套接字
包括红外线套接字(IrSock).IPX/SPX 套接字.NetBIOS 套接字.AppleTalk 套接字.ATM 套接字等.对这些套接字进行简单介绍. 第一.红外线套接字(I r S o c k) ...
- Windows网络编程笔记2
这一次看看重定向器和如何使用Netbios函数获取本机mac地址 5.获取Mac地址 利用NCBASTAT命令实现,适配器状态命令会返回一个 ADAPTER_STATUS结构,紧接着是大量 NAME_ ...
- Winsock网络编程笔记(1)----入门
今天第一次接触winsock网络编程,看的资料是Windows网络编程第二版.通过博客记住自己的看书笔记.. 在这里贴出第一个程序,虽然程序什么都没做,但以此作为入门,熟悉其网络编程风格.. #inc ...
- Linux网络编程笔记(修订版)
我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1. 基本概念 2. 基本接口 2.1. 打开一个socket 2.2. 将 ...
- storysnail的Windows串口编程笔记
storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...
- [转]Windows网络编程学习-面向连接的编程方式
直接附上原文链接:windows 网络编程学习-面向连接的编程方式
随机推荐
- Cypress测试工具
参考博客: https://testerhome.com/articles/19035 最近一段时间学习了cypress的测试工具, 她是一个端到端的测试web工具. 环境准备 1.工具:vs co ...
- JS浏览器获取宽高
screen.availHeight is the height the browser's window can have if it is maximized. (including all th ...
- linux下mysql多实例安装(转)
转自:http://www.cnblogs.com/xuchenliang/p/6843990.html 1.MySQL多实例介绍 1.1.什么是MySQL多实例 MySQL多实例就是在一台机器上 ...
- ASP.NET Dev ASPxGridView控件使用 ASP.NET水晶报表打印
1.ASPxGridView控件使用 2.ASP.NET水晶报表客户端打印 3.javascript打印 4.ASPxGridView根据Textbox查询 5. ASPxGridView 列宽 1. ...
- 怎么旋转PDF文件的方向并保存成功
http://jingyan.baidu.com/article/59a015e39d7802f79488651e.html PDF格式的文档是非常普遍的一种阅读电子书格式,基本上非常好用了,不过有时 ...
- c++ 输入split
日期格式为“yyyy/mm/dd”(即年/月/日)格式 scanf("%d/%d/%d", &year, &month, &day);
- POJ 3181 Dollar Dayz(递推,两个long long)
题意:John有N美元,有价格为1~K的工具,可以买的个数不限,问1~K组合出N的方案数. f[i = 第i中工具][j = 花费为j] = 方案数. f[i][j] = sigma{ f[i-1][ ...
- 2018.10.05 TOPOI提高组模拟赛 解题报告
得分: \(100+5+100=205\)(真的是出乎意料) \(T1\):抵制克苏恩(点此看题面) 原题: [BZOJ4832][Lydsy1704月赛] 抵制克苏恩 应该还是一个比较简单的\(DP ...
- 在Drupal7中创建web接口
Services 模块允许您从一个主要模块后端配置和管理区域启用您 Drupal 站点上自定义构建的内容服务器和服务.该模块中包含的服务允许您调用内容,从 Drupal 的默认和分配的 File.Co ...
- Drupal的入门学习
1. 注意content中的区别 Article和Basic page的区别 a.输入字段不一样,Article内容多了两个字段:tag和图片. b.内容的默认设置不一样,Article默认允许评论, ...