056_远程控制软件(控制远程按键)
1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
远程按键控制的实现
5.1 使用Winsocket建立控制端和被控制端连接
首先,客户端和服务器端都要创建一个数据套接字。接着,服务器调用bind()函数给套接字分配一个公认的端口。这样,客户端和服务器端就使用同样的端口来表示服务器套接字。一旦服务器将公认端口分配给了套接字,客户端和服务器端就都能使用sendto()和recvfrom()来发送和接收数据报直到完成传递。然后调用close socket来关闭套接字。
5.1.1 初始化Winsock
WSAStartup()函数的原型声明如下:
Int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData );
该函数共有两个参数。一个参数是一个WORD(双字节)型数值,他指应用程序中要使用的WinSock规范的最高版本。其中主版本在低位字节,副版本号在高位字节。第二个参数指一个指向WSDATA结构的指针,该结构的定义如下:
Typedef struct WSAData
{
WORD wVersion;
WORD wHighVersion;
Char szDescription[WSADESCRIPTION_LEN+1];
Char szSyestemStatus[WSASYS_ STATUS_LEN+1];
Unsigned short iMaxSocket;
Unsigned short iMaxUdpDg;
Char FAR * IpVendorInfo;
}WSADATA,FAR * LPWSADATA
think58
[资料来源:THINK58.com]
其中成员wVersion指Windows Sockets的版本号。
成员wHighVersion指Windows Sockets的最高版本。通常情况下,该成员的取值与成员wVersion相同。
成员szDescrpition指Windows Sockets实现的句柄字符串。
成员szSystemStatus指Windows Sockets的配置信息或相关状态。
成员iMaxSockets,iMaxUdpDg和lpVendorInfo保留;在Windows Socket2及以后的版本中被忽略。
我们一般采用如下代码来调用WSAStartup():
# include<winsocket.h>
//对应winSock 2,应该包含winsock2.h think58
WSADATA WSAData;
If (WSAStartup(0x0101,&WSAData))
{
//初始化winSock时发生错误
MessageBox(“不能加载windows套接字动态连接库”,“远程控制”,MB_OK);
Return;
} think58好,好think58 [来源:http://think58.com]
if(WSAData.wVersion!=0x0101)
{
//支持版本无效,报告后终止
return;
} [资料来源:http://think58.com]
这时,如果WinSock.dll或底层网络子系统没有正确初始化或没有找到,WSAtartup()将返回WSASYSNOTREADY。此外,这个函数允许应用程序协商使用某种版本的WinSock规范。通常在调用WSAStarup()时,我们应该指定想使用的WinSock最高版本。如果这个版本比任何DLL支持的版本低,WSAStartup()将返回WSAVERNOTSUPPORTED。
如果我们要求的版本高于或等于DLL(动态链接库)所支持的版本。WSAData的wVersion成员将包含应用程序应该使用的版本。而wHighVersion成员中将包含DLL所支持的最高版本号。
如果返回到wVersion成员的版本号不能被应用程序所接受,我们就应该调用WSACleanup()并退出引用程序,或找一个不同的WinSock32.DLL来试一试。当然,这时需要动态加载新的DLL,并从中调用WSAStartup ()。 think58好,好think58 [资料来源:THINK58.com]
5.1.2 创建套接字
任何应用程序在使用套接字之前,首先必须拥有一个套接字,系统调用socket函数向应用程序提供创建套接字的手段。Socket()函数的原型声明如下:
SOCKET PASCAL FAR socket (int af,int type ,int protocol);
该函数共有3参数,其中参数af指定通信发生的区域。UNIX系统支持的地址簇有AF_UNIX,AF_INET,AF_NS等。而DOS,Windows中仅支持AF_INET,它是网际网区域。
参数type 描述建立的套接字类型。也就是指流式套接字还是数据报套接字。
参数protocol说明该套接字使用的特定协议,如果该参数设为0。则表示使用默认的连接模式。
Socket()函数根据这3个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字句柄。
5.1.3 指定本地地址
当一个套接字用socket()创建后,存在一个名字空间(地址簇),但它没有被命名。Bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字编号联系起来,即将名字赋予套接字。bind()函数的原型声明如下:
int PASCAL FAR bind (SOCKER s,const struct sockaddr FAR *name,int namelen);
bind()函数共有3个参数,其中参数s是由socket()函数调用返回的并且未作连接的套接字句柄。
本文来自think58 [资料来源:http://THINK58.com]
参数name是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。
参数namelen指参数name 的长度。
如果调用成功,bind ()函数返回0;否则,返回SOCKER_ERROR。
地址在建立套接字通信过程中起着重要作用,作为一个网络应用程序设计者对套接字地址结构必须有明确的认识。例如,UNIX BSD有一组描述套接字地址的数据结构,其中使用TCP/IP协议的地址结构为:
struct sockaddr_in
{
short sin_family; /* AF_INET*/
u_short sin_port: /*16位端口号,网络字节顺序*/
struct in_addr sin_addr; /*32位IP地址,网络字节顺序*/
char sin_zero[8]; /*保留*/
}
5.1.4 建立套接字连接
建立套接字连接需要使用两个函数。即connect()与accept()。这两个函数用于完成一个完整相关的建立,其中connect()用于建立连接。无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到一个数据报,其目的端口未与任何套接字建立“连接”,便能判断该端口不可操作。Accept()用于使服务器等待来自某客户进程的实际连接。 [资料来源:http://think58.com]
Connect()函数的原型声明如下:
int PASCAL FAR connect(SOCKER s,const struct sockaddr FAR *name,int namelen);
Connect()函数共有3个参数,其中参数s指要建立连接的本地套接字句柄。
参数name指对方套接字地址结构的指针,对方套接字地址长度由参数namelen说明。
如果调用成功,connect()函数返回0;否则,返回SOCKET_ERROR。在面向连接的协议中,该函数调用导致本地系统和外部系统之间连接的实际建立。
由于地址簇总被包含在套接字地址结构的前两个字节中,并通过socket()函数调用某个协议簇相关。因此bind()和connect()不需要协议作为参数。
Accept()函数的原型声明如下:
SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR* addr,int FAR*addrlen);
该函数也有3个参数,其中参数s为本地套接字句柄,在用做accept()函数调用的参数前应该先调用listen()。
参数addr是指向客户方套接字地址结构的指针,用来接收连接实体的地址。Addr的确切格式由套接字创建时建立的地址簇决定。
参数addlen为客户方套接字地址的长度(字节数)。
如果调用成功,accept()函数返回一个SOCKET类型的值,表示接收到的套接字的句柄;否则,返回INVALID_SOCKET。
copyright think58 [来源:http://www.think58.com]
Accept()用于面向连接的服务。参数addr和addrlen存放客户方的地址信息。调用前,参数addr指向一个初值为空的地址结构,而addrlen的初始值为0;调用accepet()函数后,服务器等待从编号为s的套接字上接受客户连接的请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr和addrlen,并创建一个与s有相同性质的新套接字号。新的套接字可用于处理服务器并发请求。
Socket(),bind(),connect(),accept()这4个套接字系统调用可以完成一个完整五元通信(协议,本地主机地址和端口号,目的地址和端口号)相关的建立。Socket()指定五元组中的协议元,它的用法与是否为客户机或服务器,是否面向连接无关。Bind()指定五元中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关;在服务器方无论是否面向连接,均要调用bind();在客户方,若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind ()以获得一个唯一的地址。
5.1.5 监听连接
建立连接之后,服务端套接字要调用listen()函数,此调用用于面向连接服务器,表明它愿意接收连接。Listen()需在accept()之前调用,listen ()函数的原型声明如下: think58.com [版权所有:http://think58.com]
int PASCAL FAR listen(SOCKET s, int backlog);
该函数共有两个参数,其中参数s标识一个本地已建立但尚未连接的套接字句柄,服务器愿意从它上面接受请求。
参数backlog表示请求连接队列的最大长度,用于限制排队请求的个数。目前允许的最大值为5。
如果调用成功,listen()函数返回零;否则,返回SOCKET_ERROR。
Listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必需的连接,并建立长度为backlog的请求连接队列。
调用listen()是服务器接收一个连接请求的4个步骤中的第3步。它在调用socket(),分配一个流套接字且调用bind()给s赋予一个名字之后调用,而且一定要在accept()之前调用。
5.1.6 关闭套接字
closesocket()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接。则该连接被释放。Closesocket()函数的原型声明如下:
BOOL PASCAL FAR closesocket(socker s);
该函数只有一个参数s,它指待关闭的套接字句柄。
如果调用成功,closesoker()返回零;否则,返回SOCKET_ERROR。
5.2 按键事件处理
5.2.1 数据传输
当一个连接建立好以后,就可以传输数据了,常用的函数调用有send()和recv()。 copyright think58 [版权所有:http://think58.com]
Send()调用用于在参数s指定的已连接的数据报或流套接字上发送输出数据,其原型声明如下:
Int PASCAL FAR send(SOCKET s,const char FAR *buf,int len,int flags);
该函数也有4个参数,其中参数s为已连接的套接字句柄。
参数buf指向接收输入数据缓冲区的指针,其长度由len指定。
参数flags指定传输控制方式,如是否接收带外数据等。
如果调用成功,recv()函数返回总共接收的字节数;如果连接被关闭,返回零。否则,返回SOCKET ERROR。
为了从套接字中接受数据,可以使用recv()函数。其函数原型声明如下:
int recv (SOCKET s, char FAR * buf , int len,int falgs):
recv()函数有4个参数,其中参数s指套接字句柄。
参数buf和len分别指将要接收数据的缓冲区及缓冲区长度。
参数flags可被设置成MSG-OOB,用来接收带外数据,或设置成MSG-PEEK用来向缓冲区填入收到的数据,而且仍按照数据的输入顺序进行填充。
对套接字来说,如果数据是按输入的队列接收过来的,recv()函数将返回读入数据的字节数,否则,recv()将返回SOCKET—ERROR。
5.2.2 模拟按键
函数keybd_event()综合一个按键事件,系统用这个事件产生WM_KEYUP或WM_KEYDOWN消息.在WindowsNT下,该函数被SendInput()所代替.函数keybd_event()的原型如下: [资料来源:www.THINK58.com]
VOID keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD dwExtraInfo);
函数keybd_event()有4个参数,其中参数bVk指所按键的虚拟键值.取值范围在1至254之间。常见的特殊键值有:Tab(9),Shift(16),Ctrl(17),Alt(18),CapsLock(20),Esc(27), Win(91,92),NumLock(144)及ScrollLock(145)等.
参数bScan指所按键的扫描码。
参数dwFlags指函数操作标志位集合,应用程序可以检测这个值。它可以取以下值。
KEYEVENTF_EXTENDEDKEY:如果指定该值,则键盘扫描码加一个前缀(224)。
KEYEVENTF_KEYUP:如果指定该值,则键释放,否则,键一直被按下。
参数dwExtralnfo指向一个附加的与键值有关联的32位值。
5.3具体代码
由于篇幅有限,这里只列出了部分重要代码。
服务器端套接字的创建,绑定,连接:
char chName[256];
sockaddr_in addr;
hostent* pEnt = NULL;
int addrlen = 0, nRet = 0;
nRet = ::gethostname(chName, 256);
if(nRet == 0)
{
m_sckServer = socket(AF_INET, SOCK_STREAM, 0);
if(m_sckServer != INVALID_SOCKET)
{
pEnt = ::gethostbyname(chName);
if(pEnt)
copyright think58 [来源:http://think58.com]
{
addr.sin_family = AF_INET;
addr.sin_port = htons(m_uPort);
addr.sin_addr.s_addr = INADDR_ANY;
本文来自think58 [资料来源:http://www.THINK58.com]
nRet = ::bind(m_sckServer, (sockaddr*)&addr, sizeof(addr));
if(nRet != SOCKET_ERROR)
{
nRet = ::listen(m_sckServer, SOMAXCONN);
if(nRet != SOCKET_ERROR)
{
addrlen = sizeof(addr);
TRACE(_T("开始了服务,等待客户......\n"));
m_sckClient[0] = ::accept(m_sckServer, (sockaddr*)&addr, &addrlen);
if(m_sckClient[0] != INVALID_SOCKET)
{
//为了同步
::PL_SendSocketData(m_sckClient[0], NULL, 0, PL_TEMP, MSG_OOB);
}
m_sckClient[1] = ::accept(m_sckServer, (sockaddr*)&addr, &addrlen);
if(m_sckClient[0] != INVALID_SOCKET && m_sckClient[1] != INVALID_SOCKET)
TRACE(_T("客户连接上了.......\n"));
think58.com
[资料来源:http://THINK58.com]