相对优先级的功能。可以调用SetThreadPriority函数设置一个线程的优先级。
BOOL SetThreadPriority(HANDLE hThread, int nPriority);
3. 工作线程和用户界面线程各有什么作用?如何创建工作线程和用户界面线程? 答:线程被分为两种:用户界面线程和工作线程(又称为后台线程)。 用户界面线程通常用来处理用户的输入并响应各种事件和消息,其实,应用程序的主执行线程CWinAPP对象就是一个用户界面线程,当应用程序启动时自动创建和启动,同样它的终止也意味着该程序的结束,进程终止。
工作线程没有消息机制,用来执行程序的后台处理任务,比如计算、调度、对串口的读写操作等,它和用户界面线程的区别是它不用从CWinThread类派生来创建,对它来说最重要的是如何实现工作线程任务的运行控制函数。
在MFC中,一般用全局函数AfxBeginThread来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作线程和用户界面线程。
? CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, PVOID pParam, nPriority=THREAD_PRIORITY_NORMAL, UINT nStackSize=0,
DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL); ? CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,
int nPriority=THREAD_PRIORITY_NORMAL, UINT nStackSize=0,
DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL); 4. 多线程之间如何实现通信?
答:使用全局变量进行通信由于属于同一个进程的各个线程共享操作系统分配该进程的资源,解决线程间通信最简单的一种方法是使用全局变量。
也可以在一个线程的执行函数中向另一个线程发送自定义的消息来达到通信的目的。一个线程向另外一个线程发送消息是通过操作系统实现的。利用Windows操作系统的消息驱动机制,当一个线程发出一条消息时,操作系统首先接收到该消息,然后把该消息转发给目标线程,接收消息的线程必须已经建立了消息循环。
5. 什么是线程同步?线程同步有哪几种技术?
答:使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,这里介绍最常用的四种: 事件(CEvent)、 临界区(CCriticalSection )、 互斥(CMutex) 和信号量(CSemaphore)。
6. 线程同步中什么是“事件”技术?如何使用CEvent 类?
答:事件是一个允许一个线程在某种情况发生时,唤醒另外一个线程的同步对象。 在MFC中,CEvent 类对象有两种类型:人工事件和自动事件。
CEvent 类使用的主要函数有构造对象函数、设置事件函数、重置事件函数。 7. 线程同步中什么是“临界区”技术?如何使用CCriticalSection类?
答:临界区是一种保证在某一时刻只有一个线程能访问数据的简便办法。同一时刻临界区的数据是不会被多个线程同时访问的。
临界区类CCriticalSection的用法非常简单,步骤如下: 定义全局对象 ? 获得临界区对象
? 在线程中调用该函数来使线程获得它所请求的临界区 ? 释放临界区
8. 线程同步中什么是“互斥体”技术?如何使用CMutex类?
答:互斥体与临界区对象很像。互斥对象与临界区对象的不同在于:互斥对象可以在进程间使用,而临界区对象只能在同一进程的各线程间使用。通过CMutex来完成线程间的互斥。
为了访问一个互斥对象,务必建立一个CSingleLock或CMultiLock对象,用于访问控制。释放互斥是通过调用CSingleLock的成员函数UnLock()来实现的。 9. 线程同步中什么是“信号量”技术?如何使用CSemaphore 类?
答:当需要一个计数器来限制可以使用某个线程的数目时,可以使用“信号量”对象。CSemaphore 类的对象保存了对当前访问某一指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程的数目。
CSemaphore 类的构造函数原型如下:
CSemaphore (LONG lInitialCount=1, LONG lMaxCount=1, LPCTSTR pstrName=NULL, LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);
用CSemaphore 类的构造函数创建信号量对象时要同时指出允许的最大资源计数和当前可用资源计数。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。
第10章 网络编程
1. 简述TCP/IP模型。
答:TCP/IP通常被认为是一个四层协议系统,如图所示。 链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其它任何传输媒介)的物理接口细节。 网络层,有时也称作互联网层,处理分组在网络中的活动,
例如分组的选路。在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(Internet互联网控制报文协议),以及IGMP协议(Internet组管理协议)。
传输层主要为两台主机上的应用程序提供端到端的通信。在TCP/IP协议族中,有两个不同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议),这两种运输层协议分别在不同的应用程序中有不同的用途。
应用层负责处理特定的应用程序细节。几乎各种不同的TCP/IP实现都会提供Telnet远程登录、FTP文件传输协议、SMTP简单邮件传送协议和SNMP简单网络管理协议。 2. IP数据报格式如何?UDP数据报格式如何?TCP数据报格式如何? 答:
UDP首部格式
3. 什么是WinSock?如何加载WinSock?如何释放WinSock?如何获取WinSock中的返回错误?
答:WinSock是网络编程接口,而不是协议。
WinSock开发需要的文件包括(WinSock2.0):头文件Winsock2.h,库文件WS2_32.LIB,动态库W32_32.DLL。WinSock应用都必须加载WinSock DLL的相应版本。如果调用WinSock之前,没有加载WinSock库,这个函数就会返回一个SOCKET_ERROR。加载WinSock库是通过调用WSAStartup函数实现的。这个函数的定义如下:
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
在应用程序关闭套接口后,还应调用WSACleanup终止对WinSock DLL的使用,并释放资源,以备下一次使用。
对编写成功的WinSock应用程序而言,错误检查和控制是至关重要的,如果调用一个WinSock函数,错误情况发生了,就可用WSAGetLastError函数来获得一段代码,这段代码表明发生的状况。
4. 什么是套接字?有哪几种类型的套接字?如何创建这些套接字?
答:套接字是网络通信中一个端点的抽象,描述一个网络应用程序与其它另一个网络应用程序进行网络通信的通信对象。
套接字分为流套接字、数据报套接字和原始式套接字。
创建方法一致,均是调用socket函数,只是创建时使用的参数不同而已。
5. 请写出socket、bind、listen、connect、accept、send、sendto、recv、recvfrom、closesocket和setsockopt函数的形式,并解释各函数中的参数意义? 答:
? SOCKET socket(int af, int type, int protocol );
af指定了套接字要使用的地址类型,sockets接口设计者使用协议和地址族概念使socket接口可运行在多个网络上。AF_INET表示Internet(TCP/IP)协议族。红外套接字使用AF_IRDA(2.0)type表示创建的套接字类别,流式用SOCK_STREAM,数据报用SOCK_DGRAM。protocol用来指定此套接字使用的协议。流式用IPPROTO_TCP,数据报用IPPROTO_UDP。如果为0,表示默认为TCP/IP协议。
? int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );
服务器端调用bind()函数,调用它是将上步socket()调用成功返回的套接字描述符和一个指针传递给一个地址结构,同时也传递地址结构的长度。s是调用socket()时返回的值,地址结构通常会随网络协议的不同而不同。成功则返回0
int listen ( SOCKET s, int backlog );
建立与客户机的连接,第二个参数指明WinSock应用程序能接受连接请求队列最大值 成功则返回0
? SOCKET accept ( SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen );
第一个参数是指我们对其调用了listen()函数的套接字的描述符。第二、三个参数意义用来指定客户机套接字缓冲区的地址和长度。为NULL表示不需要客户Socket的相关信息函数返回一个新的socket用于和请求的客户连接
int connect ( SOCKET s, const struct sockaddr FAR* name, int namelen ); 第一个参数是指前面建立的套接字。第二、三个参数意义用来指定服务器套接字的地址和长度。成功返回为0
? int recv ( SOCKET s, char FAR* buf, int len, int flags );
s 是要读的套接字描述符。buf 是要读的信息的缓冲。len 是缓冲的最大长度。flags 设置为0。 返回接收的字节数
? int recvfrom ( SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int
FAR* fromlen );
前四个和recv一样.from 是一个指向数据结构 struct sockaddr 的指针,内容是源机器的 IP 地址和端口信息。fromlen 是个 int 型的指针,它的初始值为 sizeof(struct sockaddr)。 ? int send ( SOCKET s, const char FAR * buf, int len, int flags );
s 是你想发送数据的套接字描述符(或者是调用 socket() 或者是 accept() 返回的) 。buf是指向你想发送的数据的指针。len 是数据的长度。flags 设置为 0。返回为发送的字节数 ?
int sendto ( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen ); 前四个和send一样。to 是个指向数据结构 struct sockaddr 的指针,它包含了目的地的 IP 地址和端口信息。tolen 可以设置为 sizeof(struct sockaddr)。 6. 数据报套接字通信模型是如何工作的?使用WinSock如何实现? 答:数据报套接字编程的基本原理比较简单,它遵循以下的模型来实现: ? 服务器用socket函数创建,用bind函数绑定; ? 用recvfrom函数从网络上接收,收不到进入阻塞; ? 客户端也用socket函数创建套结字和 bind函数绑定; ? 客户端用 sendto函数发送数据; ? 服务器端接收数据,用sendto函数进行响应; ? 数据发送完两者调用closesocket函数进行套接字释放。 7. 流套接字通信模型是如何工作的?使用WinSock如何实现? 答:流套结字的编程模型的流程如下: ? ? ? ?
使用socket函数服务器进程创建套接字;
调用bind函数将本地地址绑定到所创建的套接字;
使用listen和accept将套接字置入监听模式并准备接受连接请求; 客户进程调用socket函数创建客户端套接字;
? 客户向服务进程发出连接请求,通过connect函数实现; ? 当服务器接收到客户请求时,阻塞的accept函数返回新一个新的套接字,并使用此套结字与客户套接字建立连接,并向客户端返回接受信号; ? 客户套接字收到服务器接受信号,表示连接建立成功,双方可以使用send和recv进行收发数据;
? 使用closesocket关闭套接字。
8. CSocke类中有哪些重要的函数?各有什么意义?
答:BOOL Create(UINT nSocketPort=0,int nSocketType=SOCK_STREAM,
longlEvent=FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE,LPCTSTR lpszSocketAddress=NULL);
nSocketPort:为套接字指定的端口,缺省时将自动为套接字选择可用的端口。 nSocketType:套接字类型,默认为字节流方式,如果把该参数设为SOCK—DGRAM,将创建数据报式套接字。
lEevent:该参数用于指定可以通知相关窗口的消息,缺省为列出的所有事件都将生成相应的消息通知相关的窗口。
lpszSocketAddress:套接字的网络地址,缺省时为本机的网络地址。 对于客户端程序,需要调用Connect成员函数连接到服务器,如下程序所示: Connect(addr,nPort);
addr:服务器地址,例如“202.12.15.1”或www.microsoft.com之类的网络地址。
nPort:服务器Socket端口号。
对于服务器端程序,需要调用Listen成员函数对Socket所在的端口进行监听,一旦收到连接请求,则调用Accept成员函数开始接收,如以下程序所示:
ServerSocket.Listen();
ServerSocket.Accept(CAsyncSocket& rConnectedSocket); Accept函数的参数为一个空的CAsyncSocket对象,即由CAsyncSocket的构造函数构造的还未调用Create成员函数创建套接字的CAsyncSocket对象。
调用其它的CasyncSocket成员函数进行通信管理,调用Send和SendTo函数用于发送数据,调用Receive和ReceiveFrom函数用于接收数据;
网络通信结束后,对于在栈中创建的CAsyncSocket对象,如果对象超出定义的范围时将自动调用析构函数释放相关资源;对于在堆中创建的CAsyncSocket,需要调用delete操作销毁对象,释放相关资源。
9. 什么是WinInet编程?其编程模型如何?
答:MFC类库包含 Win32 Internet 扩展接口,也就是WinInet,专门用于Internet客户端程序。一般来说,使用WinInet的编程模型如下: ? 创建CInternetSession类对象,创建并初始化 Internet会话;
? 建立CInternetSession对象与服务器连接; ? 连接时通过调用CInternetSession::QueryOption或 CInternetSession::SetOption查询或设置Internet请求选项;
? 反馈当前数据处理的进程信息,调用 OnStatusCallback来处理; ? 获取服务器的CInternetFile实例;
? 得到服务器文件后,就可以通过调用文件对象的Read , Write函数读取; ? 使用CInternetException类对象处理所有可能出现的常规Internet异常类型。
10.编写一个基于单文档的程序,采用流套接字通信模型,实现一个网络对战的五子棋游戏。 答:略
11.编写一个对话框程序,实现网络中的点对点的文件传输。 答:略
12.采用WinInet编程技术,创建一个基于对话框的程序,实现一个简易的FTP客户端。 答:略
13.采用流套接字编程模型,创建一个基于对话框的程序,实现一个简易端口扫描程序。 答:略
14.使用原始套接字,编写一个Ping程序。
答:略
15.采用数据报套接字编程模型,创建一个基于对话框的程序,实现一个广播程序。 答:略
第十一章习题 参考答案
1. 串口的RxD线的作用是什么?
答:RXD:数据接收;数据传输线有2条。RxD线传输发到本机的数据, 2. 串口的TxD线的作用是什么?
答:TXD:数据发送;数据传输线有2条。TxD线传输从到本机发出的数据 3. 什么是波特率?