CS模式的远程控制
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
大学
综合课程设计报告题目基于C/S的远程控制程序
学院(系)
年级专业
学生姓名
指导教师
日期
摘要 (3)
1绪论 (4)
1.1 远程控制程序研究的背景 (4)
1.2 远程控制程序研究的现状 (4)
1.3 远程控制程序设计的目的 (5)
2 远程控制程序的需求分析 (5)
3 远程控制程序的项目设计 (6)
3.1 Server Remote Control所需要的模块 (6)
3.1.1 网络模块 (6)
3.1.2 编码/解码模块 (6)
3.1.3 主框架模块 (6)
3.2 Client Remote Control所需要的模块 (7)
3.2.1 网络模块 (7)
3.2.2 编码/解码模块 (7)
3.2.3 主框架模块 (7)
3.2.4 各种对话框模块 (7)
3.3 Server程序的框架 (7)
3.3.1 窗体/视图/文件 (7)
3.3.2 网络连接和数据交换 (7)
3.3.3 数据编码解码 (8)
3.3.4 屏幕区域数据和操作命令的链表结构 (8)
3.4 Client程序的框架 (8)
3.4.1 窗体/视图/文件 (8)
3.4.2 网络连接和数据交换 (8)
3.4.3 数据编码解码 (8)
3.4.4 屏幕区域数据和操作命令的链表结构 (8)
3.4.5 其他选项对话框 (8)
3.5 远程监控程序的工作流程 (9)
4远程控制程序项目的实现 (10)
4.1 相关函数的功能与作用 (10)
4.1.1 keybd_event函数 (10)
4.1.2 mouse_event函数 (10)
4.2 屏幕区域数据和操作命令的链表结构 (11)
4.2.1 操作命令链表 (11)
4.2.2 GDI链表 (11)
4.3 网络连接和数据交换 (12)
4.3.1 LoadWinsock函数 (12)
4.3.2 ClientThread函数 (14)
4.3.3 DispatchWMMessage函数 (16)
4.3.4 Transmit函数 (16)
4.3.5 UpdataRegionalScreen函数 (17)
5 远程控制程序的项目测试 (18)
6 心得体会 (18)
7 参考文献 (19)
摘要
从上个世纪90年代以来,随着科学技术的迅速发展,人们的生产行为、生活方式都发生了重大的变化,作为生活生产中非常重要的一项技术即监控技术的重要性正在逐渐被人们所认识和重视。
监控系统的演变,是一个从集中监控向网络监控的发展历史。
早期的监控系统,采用大型仪表集中对各个重要设备的状态进行监视,并通过操作盘来进行集中式操作。
而计算机监控系统是以监测控制计算机为主体,加上检测装置、执行机构与被监测控制的对象共同构成的整体。
在该系统中,计算机实现了生产过程的检测、监督和控制功能。
在现代企业的生产和管理中,大量的物理量、环境参数、工艺数据、特性参数需要进行实时检测、监督管理和自动控制。
由于工业生产过程控制要求的高环境适应性、高实时性、和高可靠性等特点,自动控制与检测技术一直沿着自己的道路发展,测控领域所使用的通信技术都自成体系,许多通信协议不开放,而且大多数系统都是面向单台,或单一类型的设备。
【关键词】监控系统计算机实时检测监督管理自动控制适应性实时性可靠性
1绪论
1.1 远程控制程序研究的背景
随着社会的发展,人民生活水平的提高,人们拥有计算机的现象越来越普遍,但是许多用户对电脑知道的很少,当遇到问题时他们必须向无法看到电脑屏幕的技术人员描述问题的症状,并且严格遵守技术人员的指示,精确地描述屏幕上的内容,而这一点对大多数没有什么电脑知识的用户来说很难做到。
因此,远程控制技术的出现就很好的解决了这个问题,通过远程控制,技术人员就可以远程的控制用户的电脑,很快找到问题的所在。
当然,远程控制技术不仅仅有远程技术支持功能,还有远程办公,远程维护和管理的功能。
1.2 远程控制程序研究的现状
远程监控系统有两种类型,一种是生产现场没有现场监控系统,而是将数据采集后直接送到远程计算机进行处理,这种远程监控与一般的现场监控没有多大的区别,只是数据传输距离比现场监控系统要远,其它部分则和现场监控系统相同;另一种是现场监控与远程监控并存。
一般是采用现场总线技术将分布于各个设备的传感器、监控设备等连接起来,这样就从分立单元阶段进入了集成单元阶段,然后各个管理站点的服务再用局域网连接起来,这样就形成了企业内部网(Intranet)。
由于建立了基本的网络信息基础结构,设备监测、维护技术进入了集成系统阶段,在一个单位的内部基本上实现了资源和信息共享。
基于B/S和C/S的远程监控系统是以网络作为通信平台的监控系统,以HTTP技术为基础,具有简单、高效等优点,已经成为信息网络的一种最普遍应用的信息交互平台。
利用网络通信技术Socket技术、数据采集技术及面向对象等软件技术实现了整个系统的系统管理、用户管理、设备监控数据显示及报警等模块,其优点是充分利用了现有的局域网资源和广域网资源,以最高的性能价格比,以信息的实时获取和实时控制为中心,实现信息、资源及任务的综合共享和全局一体化的管理。
例如:监控系统将设备运行情况提供给服务器,并由服务器发送到各个节点客户机,工作人员在客户机端(一般为远端)便可了解整个系统的工作状态及运行情况。
简单地讲,对企业来说就是充分利用现代技术解决实时数据的采集、传输和处理以及进行实时控制的问题。
正是它的这些优点使得它得以飞速发展。
随着网络技术的不断发展,远程监控将更多地应用在企业生产过程的管理中,专业技术人员可以通过互联网来管理和维护生产过程,优化生产工艺,提高设备的可用率,最终降低生产成本,提高效益。
1.3 远程控制程序设计的目的
通过此次综合课程设可以使我对基于C/S的远程控制有进一步的了解,了解远程控制的工作原理和具体应用,可以更好的学习Winsock编程技术,并提高了查阅、运用资料的能力。
2 远程控制程序的需求分析
基于C/S的远程控制程序实现的是在网络上由一台电脑(主控端/客户端)远距离去控制另一台电脑(被控端/服务器端)的技术。
这里的远程不是字面意思的远距离,一般指通过网络控制远端电脑。
不过,大多数时候所说的远程控制往往指在局域网中的远程控制而言。
当操作者使用主控端电脑控制被控端电脑时,就如同坐在被控端电脑的屏幕前一样,可以启动被控端电脑的应用程序,可以使用被控端电脑的文件资料,甚至可以利用被控端电脑的外部打印设备和通信设备进行打印和访问互联网,就像利用遥控器遥控电视的音量、变换频道或者开关电视机一样。
不过,主控端电脑只是将键盘和鼠标的指令传送给远端电脑,同时将被控端电脑的屏幕画面通过通信线路回传过来,从而达到控制和被控制的目的。
远程控制软件一般分为两个部分:客户端程序和服务器程序。
在使用前需要将客户端程序安装到主控端电脑上,将服务器端程序安装到被控端电脑上。
控制的过程一般是现在被控端电脑上运行服务器端程序,然后在控制端电脑上执行客户端程序,与服务器端程序建立起一个特殊的远程服务,然后通过这个远程服务,使用各种远程控制功能发送远程控制命令,控制服务器中的各种程序的运行。
3 远程控制程序的项目设计
远程监控程序的思想是通过Socket和应用程序的消息机制,监控程序的客户端与被监控程序的服务器端进行数据交换,达到监控和被监控的目的。
根据这个思想,需要两个独立的程序,Server Remote Control和Client Remote Control。
3.1 Server Remote Control所需要的模块
3.1.1 网络模块
网络模块负责监听客户端的连接,负责接收并处理客户端发送过来的命令(鼠标、键盘、网格消息等命令),在本机上处理这些消息并发送各种数据(屏幕数据、命令反馈数据等)到客户端监听程序。
3.1.2 编码/解码模块
编码/解码模块负责对数据进行霍夫曼压缩,Run Length编码解码。
这个模块是根据客户端程序的要求(客户端发送的通用命令消息、压缩方法消息,网格消息,颜色模式消息)采用具体的编码解码方法。
选项包括霍夫曼压缩、多遍霍夫曼压缩、RunLength编码、RunLength-霍夫曼组合编码以及无压缩。
3.1.3 主框架模块
主框架模块负责Server端窗口消息处理和映射。
3.2 Client Remote Control所需要的模块
3.2.1 网络模块
网络模块负责连接到服务器端的监听Socket;负责向服务器端发送各种操作命令(鼠标、键盘、各种通用参数消息等命令);负责接收从服务器端发过来的数据(屏幕数据、命令反馈数据等),并对这些数据进行解码解压缩,然后将这些数据的内容(屏幕内容)显示在本机上。
3.2.2 编码/解码模块
编码/解码模块负责对数据进行霍夫曼解码、Run Length解码。
这个模块是根据用户在“数据压缩”对话框中的选择决定客户端程序解码方法,并发送具体的编码解码方法到客户端。
选项包括霍夫曼压缩、多遍霍夫曼压缩、RunLength编码、RunLength-霍夫曼组合编码以及无压缩。
3.2.3 主框架模块
主框架模块负责Client端窗口消息处理和映射。
3.2.4 各种对话框模块
对话框模块包括色彩模式选择对话框,编码解码选择对话框,网格数目选择对话框。
3.3 Server程序的框架
3.3.1 窗体/视图/文件
MainWnd.h/MainWnd.cpp:显示主窗体界面,窗口消息处理。
WndProc.h/WndProc.cpp:Windows程序入口。
3.3.2 网络连接和数据交换
Server.h/Server.cpp:用Winsock处理网络连接和数据传输,完成从客户端发送过来的消息处理。
3.3.3 数据编码解码
HuffCompress.h/HuffCompress.c:完成霍夫曼压缩的算法。
RLE.h/RLE.c:完成RunLength编码解码的算法。
3.3.4 屏幕区域数据和操作命令的链表结构
Command.h/Command.c:用于Server和Client交互的消息的链表。
GDI.h/GDI.c:用于绘制屏幕的屏幕数据链表。
3.4 Client程序的框架
3.4.1 窗体/视图/文件
MainWnd.h/MainWnd.cpp:显示主窗体界面,窗口消息处理。
WndProc.h/WndProc.cpp:Windows程序入口。
3.4.2 网络连接和数据交换
Client.h/Client.cpp:用Winsock处理网络连接和数据传输,完成从屏幕的绘制。
3.4.3 数据编码解码
HuffCompress.h/HuffCompress.c:完成霍夫曼压缩的算法。
RLE.h/RLE.c:完成RunLength编码解码的算法。
3.4.4 屏幕区域数据和操作命令的链表结构
Command.h/Command.c:用于Server和Client交互的消息的链表。
GDI.h/GDI.c:用于绘制屏幕的屏幕数据链表。
3.4.5 其他选项对话框
ColorMode.h/ColorMode.cpp:色彩模式选择的对话框。
Compression.h/Compression.cpp:压缩模式选择的对话框。
GridSpacing.h/GridSpacing.cpp:网格空间选择的对话框。
ServerIP.h/ServerIP.cpp:服务器IP和端口选择的对话框。
3.5 远程监控程序的工作流程
首先启动Server Remote Control程序,开启监听端口,将Client Remote Control连接到服务器的端口,之后Server开启一个与客户端通信的线程。
这个线程专门负责接收和处理客户端发送过来的消息,并在本机上执行相关的命令(鼠标、键盘、文件等),然后将当前改变的区域的屏幕显示的内容发送到客户端。
而当客户端连接到服务器端之后,客户端程序就可以根据服务器端传过来的屏幕数据在本地机上显示出屏幕的内容,从而做出各种鼠标、键盘操作以及一些通用信息的更新(颜色模式选择,压缩模式选择,网格数目等),这些操作命令会通过Socket传递到服务器端,服务器端在根据这些命令做出相应的操作。
需要注意的是,服务器刷屏的时候并不需要将整个屏幕的内容都发送到客户端,因为整个屏幕图片的数据量是比较大的,对于当前网络传输技术来说,传输这么大的数据会感到明显的迟滞从而影响远程控制的效果。
一个可行的办法是将屏幕划分成网格状。
屏幕的刷新是以网格为单位的,只需要将那些变化的屏幕网格发送到客户端即可。
4远程控制程序项目的实现
4.1 相关函数的功能与作用
4.1.1 keybd_event函数
VOID keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,ULONG_PTR dwExtranInfo); 【参数说明】
bVk:<输入>定义了一个虚键码,它的范围是1~254。
bScan:保留。
dwFlags:<输入>定义函数操作的类型,按下或者弹起。
DwExtraInfo:<输入>定义了击键的额外的关联值。
程序中如果想模拟PRINTSCRN的消息,即抓取屏幕的内容并将起保存到剪切板中,那么可以将参数bVk设置为VK_SNAPSHOT。
程序中想要模拟一个键被按下或弹起可以这样写:keybd_event((BYTE)vk,(BYTE)vk,0,0);//键按下
keybd_event((BYTE)vk,(BYTE)vk,KEYEVENTF_KEYUP,0);//键弹起
4.1.2 mouse_event函数
VOID mouse_event(DWORD dwFlags,DWORD dx,DWORD dy,DWORD dwData,ULONG_PTR dwExtraInfo);
【参数说明】
dwFlags:<输入>定义各种鼠标移动和点击的标志。
dx:<输入>定义鼠标沿着x轴的绝对位置。
dy:<输入>定义鼠标沿着y轴的绝对位置。
dwData:<输入>如果dwFlags包括了MOUSEEVENTF_WHEEL的标志,则dwData定义鼠标滚轮的运动的数量。
正值表示滚轮向前旋转;负值表示滚轮向后旋转。
滚轮被按下的标志是WHEEL_DELTA。
dwExtraInfo:<输入>定义与鼠标事件关联的额外的信息。
应用程序可以调用GetMe_ssageExtraInfo获得这些额外的信息。
某些应用程序用mouse_event函数合成鼠标的各种消息和参数。
例如,一个手写板厂商需要将手写笔的信息传递到应用程序中,那么可以写一个动态链接库直接和手写板硬件通信,从而获得额外的信息并将其保存到队列中。
动态链接库便可以使用队列中的x,y坐标和按键信息作为参数调用mouse_event.这样就可以用手写笔模拟出鼠标的所有的信息。
4.2 屏幕区域数据和操作命令的链表结构
下面的代码为服务器端和客户端共用
4.2.1 操作命令链表
操作命令链表的数据结构在Command.h定义如下:
struct CommandDS
{
char szElement[81];
};
struct CommandList
{
struct CommandDS C ommand;
struct CommandList *pNext;
};
操作命令链表的具体操作(添加节点和清空链表)在Command.h定义如下:
struct CommandList *Add_Command(struct CommandList *pNode,struct CommandDS Command);
void Clear_Command(struct CommandList *pStart);
4.2.2 GDI链表
GDI链表的数据结构在GDI.h中定义如下:
struct GdiDS
{
char *pDIBitmap;
char *pDIB;
char *pStartDIB;
int iGridX;
int iGridY;
int iWidth1;
int iWidth2;
int iHeight1;
int iHeight2;
LPBITMAPINFOHEADER lpBitmapIH;
BOOL fDIBitmap;
};
struct GdiList
{
struct GdiDS Gdi;
struct GdiList *pNext;
};
GDI链表的两个具体的操作(添加节点和清空链表)在GDI.h中定义如下:
struct GdiList *Add_Gdi(struct GdiList *pNode,struct GdiDS Gdi);
void Clear_Gdi(struct GdiList *pStart);
4.3 网络连接和数据交换
4.3.1 LoadWinsock函数
LoadWinsock函数用来装载和初始化Winsock,绑定本地地址,创建监听Socket,等候客户端连接。
它的实现为:
DWORD WINAPI LoadWinsock(LPVOID lpParam)
{
// 协议变量
LPBYTE pBuf;
WSAPROTOCOL_INFO Protocol;
int nRet;
int nZero = 0;
int iAddrSize;
HANDLE hThread;
DWORD dwThreadId;
char szClientIP[81];
char szString[255];
struct sockaddr_in local,client;
// 这个结构用来在LPARAM参数中传递信息到客户端线程
struct myStruct myStructure;
// 为协议的选择和所有协议的变量决定需要的缓冲区的大小
dwLen = 0;
nRet = WSAEnumProtocols(NULL,NULL,&dwLen);
if (nRet == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAENOBUFS)
return 1;
}
pBuf = malloc(dwLen);
// 为WSASocketGet()得到协议
nRet = SelectProtocols(SETFLAGS,NOTSETFLAGS,(LPWSAPROTOCOL_INFO)pBuf,&dwLen,&Pro tocol);
// 创建我们的监听socket
Listen = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_IP,NULL,0,SOCK_STREAM);
if (Listen == SOCKET_ERROR)
{
sprintf(szString,"socket() failed: %d",WSAGetLastError());
MessageBox(NULL,szString,"Remote Server",MB_OK);
return 1;
}
// 设置server端信息
local.sin_addr.s_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(gPort);
// 绑定到socket
if (bind(Listen,(struct sockaddr *)&local,sizeof(local)) == SOCKET_ERROR)
{
sprintf(szString,"bind() failed: %d\n", WSAGetLastError());
MessageBox(NULL,szString,"Remote Server",MB_OK);
return 1;
}
//为了减小CPU的利用率,禁止在socket上将数据发送到缓冲。
设置SO_SNDBUF为0,
//从而使winsock直接发送数据到客户端,而不是将数据缓冲才发送。
nZero = 0;
setsockopt(Listen,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
//开始监听
listen(Listen,SOMAXCONN);
iAddrSize = sizeof(client);
while (TRUE)
{
// 阻塞方式的接收客户端的连接,但因为这是一个线程函数,所以用户不会感到阻塞
Socket = accept(Listen,(struct sockaddr *)&client,&iAddrSize);
if (Socket != INV ALID_SOCKET)
{
// 设置传到客户端线程的信息的数据结构
myStructure.Socket = Socket;
myStructure.hWnd = hServerWnd;
//找出客户端的IP地址
memset(szClientIP,'\0',sizeof(szClientIP));
sprintf(szClientIP,"%s",inet_ntoa(client.sin_addr));
// 为每一个客户端创建一个线程
hThread = CreateThread(NULL,0,ClientThread,(LPVOID)&myStructure,0,&dwThreadId);
if (hThread)
{
//关闭线程句柄
CloseHandle(hThread);
}
}
else
return 1;
}
return 0;
}
4.3.2 ClientThread函数
ClientThread是一个与客户端通信的线程函数,这个函数等候从客户端程序发送过来的消息。
如果这个消息是“REFRESH”,那么它发送当前的桌面图片;如果这个消息是“DISCONNECT”,那么他结束和客户端的连接;如果这个消息以“WM_”开头,那么它就根据消息的类型,在服务器端执行该消息,并把返回的结果发送到客户端。
它的实现为:DWORD WINAPI ClientThread(LPVOID lpParam)
{
HWND hWnd;
SOCKET MySocket;
FD_SET SocketSet;
struct timeval timeout;
char szMessage[2049];
DWORD iRecv;
struct myStruct *myStructure;
DWORD iLength;
DWORD iRet;
int iUpdates;
// 分析参数
myStructure = (struct myStruct *)lpParam;
MySocket = myStructure->Socket;
hWnd = myStructure->hWnd;
// 设置超时值
_sec = 0; // 秒
_usec = 0; // 微秒
// 设置Socket集合
SocketSet.fd_count = 1;
SocketSet.fd_array[1] = MySocket;
// 轮询sockets
while(TRUE)
{
// 等候发送过来的数据直到超时
iRet = select(0,&SocketSet,NULL,NULL,&timeout);
if (iRet != 0)
{
//初始化缓冲
memset(szMessage,'\0',sizeof(szMessage));
// 阻塞方式调用recv()
iRecv = recv(MySocket,szMessage,2048,0);
szMessage[iRecv] = '\0';
CHECK_MSG:
// 是不是"REFRESH"消息
if (strncmp(szMessage,"REFRESH",7) == 0)
{
// 捕获并且发送桌面的更新的区域
iUpdates = SendRegionDisplay(hServerWnd,MySocket);
}
// 检查从客户端发送过来的Windows 命令消息,这是一个核心部分
else if (strncmp(szMessage,"WM_",3) == 0)
{
// 解析从客户端发送过来的消息并发送到本机的消息队列
DispatchWMMessage(szMessage);
// 看看是否还有消息
iLength = strlen(szMessage);
if (iLength > 0)
goto CHECK_MSG;
}
// 检查是否是查询消息
else if (strncmp(szMessage,"RESOLUTION",10) == 0)
{
SendResolution(MySocket);
}
// 检查是否是DISCONNECT消息
else if (strncmp(szMessage,"DISCONNECT",10) == 0)
{
fChange = FALSE;
fDIBitmap = FALSE;
pGdiNode = GdiStart.pNext;
while (pGdiNode)
{
free(pGdiNode->Gdi.pDIBitmap);
free(pGdiNode->Gdi.pDIBChangeStart);
pGdiNode->Gdi.fDIBitmap = FALSE;
pGdiNode->Gdi.fChange = FALSE;
pGdiNode = pGdiNode->pNext;
}
// 停止查询,相当于结束该线程
break;
}
}
}
closesocket(MySocket);
return 0;
}
4.3.3 DispatchWMMessage函数
DispatchWMMessage函数用来解析从客户端发送过来的消息,并发送到本机的消息队列,完成控制操作。
4.3.4 Transmit函数
Transmit函数将数据发送到客户端。
此函数的实现为:
BOOL Transmit(SOCKET MySocket,char *pData,DWORD dwLength)
{
WSAOVERLAPPED olSend;
WSAEVENT gheventOlSock;
WSAEVENT eventArray[2];
WSABUF buffSend;
DWORD dwRet,dwNumBytes,dwFlags;
int nWSAError;
char szError[81];
// 为发送完成创建一个信号事件
gheventOlSock = WSACreateEvent();
eventArray[0] = gheventOlSock;
// 初始化重叠发送的结构
ZeroMemory(&olSend, sizeof(WSAOVERLAPPED));
// 为发送重叠结构创建一个信号时间
olSend.hEvent= gheventOlSock;
buffSend.len = dwLength;
buffSend.buf = pData;
// 持续发送,直到dwSendLen个字节被发送完成
while (TRUE)
{
if ((dwRet = WSASend(MySocket,&buffSend,1,&dwNumBytes,0,&olSend,NULL)) == SOCKET_ERROR)
{
nWSAError= WSAGetLastError();
if (nWSAError != ERROR_IO_PENDING)
{
sprintf(szError,"WSASend failed with error %d\n",nWSAError);
MessageBox(NULL,szError,"Server",MB_OK);
}
}
if (WSAWaitForMultipleEvents(1,eventArray,FALSE,WSA_INFINITE,FALSE) == WSA_W AIT_FAILED)
{
sprintf(szError,"WSAWaitForMultipleEvents failed %d\n", WSAGetLastError());
MessageBox(NULL,szError,"Server",MB_OK);
}
// 重置gheventOlSock
WSAResetEvent(eventArray[0]);
if (WSAGetOverlappedResult(MySocket,&olSend,&dwNumBytes,FALSE,&dwFlags) == FALSE)
{
sprintf(szError,"WSAGetOverlappedResult failed with error %d\n", WSAGetLastError());
MessageBox(NULL,szError,"Server",MB_OK);
}
buffSend.len -= dwNumBytes;
if (buffSend.len == 0)
break;
else
buffSend.buf += dwNumBytes;
}
// 关闭信号事件
WSACloseEvent(gheventOlSock);
return TRUE;
}
4.3.5 UpdataRegionalScreen函数
UpdataRegionalScreen函数用来发送屏幕刷新的消息。
5 远程控制程序的项目测试
6 心得体会
忙碌了差不多两个星期,在大家的共同努力下,我总算将此程序设计出来。
尽管不是自己独立完成,但仍然很高兴,因为在设计的过程中,让我了解到要设计一个大型程序,查找资料是至关重要的,在他人的基础上,再根据自己所学进行修改与调试,最后设计出自己想要的程序,这过程艰辛,但只要你持之以恒,成功指日可待。
另外平时扎实的基础也很关键,因为如果你平时学得就不怎么样,那么你面对这么一个比较有难度的程序,你可能会望而却步,看他人的程序都是个难点,更别说让你自己去设计。
为解决此类问题,最好就是多向同学,老师请教,不要怕难为情。
尤其是老师,凭他们多年的编写程序的经验,要解决我们的问题,对他们来说只是轻而易举。
7 参考文献
[1] 丁展刘海英《Visual C++网络通信编程使用案例精选》人民邮电出版社北京2004年
[2]谭浩强《 C++程序设计》清华大学出版社北京 2006年
[3] 刘炜玮汪晓平《C语言高级实例解析》清华出版社。