MFC网络编程 MFC socket编程_第1页
MFC网络编程 MFC socket编程_第2页
MFC网络编程 MFC socket编程_第3页
MFC网络编程 MFC socket编程_第4页
MFC网络编程 MFC socket编程_第5页
已阅读5页,还剩38页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

5socket 简介在网络编程中最常用的方案便是 Client/Server (客户机/服务器)模型。在这种方案中客户应用程序向服务器程序请求服务,一个服务器程序通常用一个众所周知的地址监听对服务的请求,也就是说服务器进程一直处于休眠状态,直到一个客户向这个服务器的地址提出了连接请求。在这个时刻,服务程序被惊醒并且为客户提供服务,即对客户的请求作出适当的反应。为了方便这种 Client/Server 模型的网络编程,90年代初由微软联合了其他几家公司共同制定了一套WINDOWS 下的网络编程接口,即 Windows Sockets 规范,它不是一种网络协议,而是一套开放的、支持多种协议的 Windows 下的网络编程接口。现在的 Winsock 已经基本上实现了与协议无关,可以使用 Winsock 来调用多种协议的功能,但较常使用的是 TCP/IP 协议。Socket 实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有 Socket 接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个 Socket 接口来实现。微软为 Visual C+定义了 Winsock 类,如 CAsyncSocket 类和派生于 CAsyncSocket 的 CSocket 类,它们简单易用,可以使用这些类来实现自己的网络程序。但是为了更好的了解 Winsock API 编程技术,这里先探讨怎样使用底层的 API 函数实现简单的 Winsock 网络应用程式设计,分别说明如何在 Server 端和 Client 端操作 Socket,实现基于 TCP/IP 的数据传送,最后给出相关的代码。l 一些网络的基本概念n 同步:指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式n 异步:指的是发送方不等接收方响应,便接着发下个数据包的通信方式n 阻塞:指调用某函数时,直到该函数完成操作,才返回;否则一直阻塞在该调用上n 非阻塞:指调用某操作时,不管操作是否成功都立即返回,而不会挂在该操作上5.1 C/S模式下的socket通信C/S模式即我们平时经常提到的 客户端服务器 模式。l 网络软件的通用体系结构客户(Client)和服务器(Server)是指通信中所涉及的两个应用进程。客户端服务器方式所描述的是进程之间服务和被服务的关系。在下图中,主机A运行客户程序而主机B运行服务器程序。l 最简单的Socket通信流程一个只有客户方向服务方发信息的单向通信,并且也只有客户方会主动提出断开连接的最简单的情形(相反过程的原理是一样的),其双方Socket之间的关系如下图所示。由上面这个十分简单的过程很容易得出最简单的Socket通信流程,如下图所示。客户机/服务器模式的建立基于以下两点:l 非对等作用l 通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:首先服务器方要先启动,并根据请示提供相应服务。网络字节顺序不同的计算机存放多字节值的顺序不同,有的机器在起始地址存放低位字节(低位先存),有的机器在起始地址存放高位字节(高位先存)。基于Intel的CPU,即我们常用的PC机采用的是低位先存。为保证数据的正确性,在网络协议中需要指定网络字节顺序,TCP/IP协议使用16位整数和32位整数的高位先存格式。由于不同的计算机存放数据字节的顺序不同,这样发送方发送数据后,即使接收方接受到该数据,也有可能无法查看所接收到的数据。所以在网络中不同主机间进行通信,要统一采用网络字节顺序。大端、小端l 小端法(Little-Endian)就是低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端。 (主机字节顺序)l 大端法(Big-Endian)就是高位字节排放在内存的低地址端即该值的起始地址,低位字节排放在内存的高地址端。(网络字节顺序)举个简单的例子,对于整形0x12345678。它在大端法和小端法的系统内中,分别如图所示的方式存放。字节顺序转换Windows Sockets 的htons函数将把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,函数原型如下:u_short htons(u_short hostshort);参数:l hostshort:一个以主机字节顺序表示的16位数。与htons函数相类似的还有一个函数htonl,该函数将把一个u_long类型的值从主机字节顺序转换为TCP/IP网络字节顺序,函数原型如下:u_long htonl(u_long hostlong);参数:l hostlong:一个以主机字节顺序表示的32位数。5.2. 基于 TCP/IP 的 socket 编程TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。因此用户一般不涉及。编程时,编程界面有两种形式:一、是由内核直接提供的系统调用;二、使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。5.2.1 服务器端操作流程l 加载套接字库。在初始化阶段调用 WSAStartup()此函数在应用程序中初始化 Windows Sockets DLL,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL 中的 API 函数。该函数原型如下:int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);参数:n wVersionRequested:Socket的版本号WORD 类型、MAKEWORD、LOBYTE、HIBYTE 宏 u WORD 类型是一个 16 位的无符号整型, 在 WTYPES.H 中被定义为: typedef unsigned short WORD; 其目的是提供两个字节的存储, 在 Socket 中这两个字节可以表示主版本号和副版本号。 u 使用 MAKEWORD 宏可以给一个 WORD 类型赋值。例如要表示主版本号 2,副版本号 0,可以使用如下代码: WORD wVersionRequested; wVersionRequested = MAKEWORD(2, 0); 注意:低位内存存储主版本号2,高位内存存储副版本号0,其值为 0x0002。 u 使用宏 LOBYTE 可以读取 WORD 的低位字节HIBYTE 可以读取 WORD 的高位字节。n lpWSAData:指向一个用于存储 Socket 库信息的WSAStartup结构。返回值:n 等于0,初始化成功n 不等于0, 初始化失败l 创建套接字 Socket初始化 WinSock 的动态连接库后,需要在服务器端建立一个监听的 Socket,为此可以调用 Socket()函数用来建立这个监听的 Socket,并定义此 Socket 所使用的通信协议.此函数调用成功返回 Socket 对象,失败则返回 INVALID_SOCKET.调用 WSAGetLastError()可得知原因,所有 WinSocket 的 API 函数都可以使用这个函数来获取失败的原因。函数原型如下:SOCKET socket(int af,int type,int protocol)参数: n af: 代表网络地址族,目前只有一种取值有效,即 AF_INET, 代表 internet 地址族。n type: 代表网络协议类型,SOCK_DGRAM代表UDP协议,SOCK_STREAM代表TCP协议。 n protocol: 指定网络地址族特殊协议,目前无用,赋值0即可。如果要建立的是遵从 TCP/IP 协议的 socket,第二个参数 type 应为 SOCK_STREAM,如为 UDP(数据报)的socket,应为 SOCK_DGRAM。sockets(套接字)编程有三种:流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)。基于 TCP 的 socket 编程是采用的流式套接字。返回值:SOCKET 类型SOCKET 是 socket 套接字类型,在 WINSOCK2.H 中有如下定义: typedef unsigned u_int; typedef u_int SOCKET; 可知套接字实际上就是一个无符号整形,它将被 Socket 环境管理和使用。 套接字将被创建、设置、用来发送和接收数据,最后会被关闭。 l 将创建的套接字绑定到本机地址的某一端口上。bind接下来要为服务器端定义的这个监听的 Socket 指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此要调用 bind()函数,该函数调用成功返回 0,否则返回 SOCKET_ERROR.int bind( SOCKET s,const struct sockaddr FAR *name,int namelen);参 数: n s:被绑定的套接字n name: 是一个sockaddr结构指针,该结构中包含了要绑定的地址和端口。n namelen:第二个参数name 的长度;如果使用者不在意地址或端口的值,那么可以设定地址为 INADDR_ANY,及 Port 为 0。对于多接口主机使用INADDR_ANY指定了一个通配地址,让该主机的任何一个IP地址都匹配。Windows Sockets会自动将其设定适当之地址及 Port(1024 到 5000 之间的值)。此后可以调用 getsockname()函数来获知其被设定的值。下面对其涉及的类型作一番解析: n sockaddr_in类型: sockaddr_in 定义了socket发送和接收数据包的地址,其定义如下: strucr sockaddr_in short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero8; ; 其中 in_addr 定义如下: struct in_addr union struct u_char s_b1, s_b2, s_b3, s_b4 S_un_b; struct u_short s_w1, s_w2 S_un_w; u_long S_addr; S_un; ; 首先阐述 in_addr 的信义。 很显然它是一个存储 ip 地址的联合体,有三种表达方式: u 第一种用四个字节来表示IP地址的四个数字; u 第二种用两个双字节来表示IP地址; u 第三种用一个长整型来表示IP地址; 给 in_addr 赋值的一种最简单方法是使用 inet_addr 函数,它可以把一个代表IP地址的字符串赋值。转换为in_addr类型。如: addrServer.sin_addr = inet_addr(); 其反函数是 inet_ntoa,可以把一个 in_addr 类型转换为一个字符串。 sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下: u 第一字段 short sin_family,代表网络地址族,如前所述,只能取值AF_INET; u 第二字段 u_short sin_port,代表IP地址端口,由程序员指定; u 第三字段 struct in_addr sin_addr,代表IP地址; u 第四个字段char sin_zero8,是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。 n sockaddr 类型sockaddr 类型是用来表示 Socket 地址的类型,同 socketaddr_in 类型相比,sockaddr 的适用范围更广,因为sockeaddr_in只适用于 TCP/IP 地址。sockaddr 的定义如下: struct sockaddr ushort sa_family; char sa_data14; ; 可知sockaddr 的16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockadddr的。事实上也往往使用这种方法。l 为套接字设置监听模式,准备客户请求。listen当服务器端的 Socket 对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求.listen()函数使服务器端的 Socket 进入监听状态,并设定可以建立的最大连接数(目前最大值限制为 5,最小值为 1).该函数调用成功返回 0,否则返回 SOCKET_ERROR。int listen(SOCKET s,int backlog );参 数: n s:需要建立监听的 Socket;n backlog:最大连接个数;l 等待客户请求到来。当请求到来,将接受连接请求,并返回一个新的对应于此次连接的套接字。accept当 Client 提出连接请求时,Server 端 hwnd 视窗会收到 Winsock Stack 送来自定义的一个消息,这时可以分析 lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一 Socket 与客户端的 Socket 相通,原先监听之 Socket 继续进入监听状态,等待他人的连接要求。该函数调用成功返回一个新产生的 Socket 对象,否则返回 INVALID_SOCKET。SOCKET accept(SCOKET s,struct sockaddr FAR *addr,int FAR *addrlen );参数: n s: 监听套接字n addr:存放来连接的客户端的地址、端口信息;n addrlen:addr 的长度l 用新返回的套接字和客户端进行通信。send/recvsend函数通过一个已建立连接的套接字发送数据,函数声明如下:int send(SOCKET s, const char FAR *buf, int len, int flags);参数:n s: 是一个已建立连接的套接字。n buf:指向一个缓冲区,该缓冲区包含将要传递的数据。n len:缓冲区的长度。n flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。调用send函数向客户端发送数据,注意这个函数使用的套接字需要使用已建立连接的那个套接字,而不是用于监听的那个套接字。recv函数从一个已连接的套接字接收数据。函数原型如下:int recv(SOCKET s,char FAR* buf, int len, int flags);参数:n s:建立连接之后准备接收数据的那个套接字。n buf:指向缓冲区的指针,用来保存接收的数据。n len:缓冲区的长度。n flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。发送完数据之后还可以从客户端接收数据,这可以使用recv函数,应注意该函数的第一个参数也应该是建立连接之后的那个套接字,并且定义一个字符数组recvBuf,用来保存接收的数据。l 通信结束后,关闭套接字。 closesocket结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭 Server 端监听状态的 socket,同样也是利用此函数.另外,与程序启动时调用 WSAStartup()憨数相对应,程式结束前,需要调用 WSACleanup()来通知 Winsock Dll 释放 Socket 所占用的资源.这两个函数都是调用成功返回 0,否则返回 SOCKET_ERROR。int PASCAL FAR closesocket( SOCKET s );参数: n s:Socket 的识别码;int PASCAL FAR WSACleanup( void );n 参数: 无5.2.2 服务器端代码实现#include #include using namespace std;int main()/加载套接字库WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);err = WSAStartup(wVersion, &wsaData);/加载套接字库失败if (err != 0)return -1;/判断版本号是否正确if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/创建套接字, 流式套接字SOCKET socSev = socket(AF_INET, SOCK_STREAM, 0);/定义结构体变量,存储ip,端口号SOCKADDR_IN addr_in;addr_in.sin_family = AF_INET;/地址族addr_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);/IPaddr_in.sin_port = htons(6000);/端口/绑定套接字bind(socSev, (sockaddr*)&addr_in, sizeof(addr_in);/将套接字设置为监听模式,准备接收客户请求listen(socSev, 5);/定义结构体变量,接收客户端IP,端口号SOCKADDR_IN client_addr;int len = sizeof(client_addr);while (1)/等待客户请求cout 服务器等待客户端请求. endl;SOCKET socClient = accept(socSev, (SOCKADDR*)&client_addr, &len);char sendBuf1024;/格式化字符串sprintf(sendBuf, 这里是 %s 传智播客-C+学院!, inet_ntoa(client_addr.sin_addr);/发送数据send(socClient, sendBuf, strlen(sendBuf) + 1, 0);char recvBuf1024;/接收数据recv(socClient, recvBuf, strlen(recvBuf) + 1, 0);/打印接收到的数据cout 接收到的数据: recvBuf endl;/关闭套接字closesocket(socClient);return 0;5.2.3 客户端操作流程l 加载套接字库(WSAStartup())l 创建套接字(socket()。l 向服务器发出连接请求(connect()。对于客户端来说,它不需要绑定,可以直接连接服务器。这可以通过调用connect函数与服务器建立一个连接。其函数原型如下:int connect(SOCKET s, const struct Sockaddr FAR* name , int namelen);参数:n s:客户端用于首发数据的套接字。n name:指定网路主机的IP地址和端口信息。n namelen:第二个参数的长度。想要与服务器建立连接,首先需要定义一个地址结构体(SOCKADDR_IN)变量,并对其成员进行赋值,设定服务端的IP地址和端口号,这里的端口需要与服务器使用的端口保存一致,而且使用网路字节顺序。l 和服务器端进行通信(send()/recv().l 通信结束后关闭套接字和加载的套接字库(closesocket()/WSACleanup()。5.2.4 客户端代码实现#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;/初始化版本号信息wVersion = MAKEWORD(1, 1);/加载套接字库err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/检测套接字版本信息if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/创建套接字SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);/定义结构体变量,存储ip,端口号SOCKADDR_IN addr_in;addr_in.sin_family = AF_INET;/地址族addr_in.sin_addr.S_un.S_addr = inet_addr();/IPaddr_in.sin_port = htons(6000);/端口号 与服务器相同/向服务器发送连接请求connect(sock, (sockaddr*)&addr_in, sizeof(addr_in);/接收数据char recvBuf1024;recv(sock, recvBuf, strlen(recvBuf) + 1, 0);cout 接收到服务器数据: recvBuf endl;/向服务器发送数据char sendBuf = Windows Socket 通信测试程序;send(sock, sendBuf, sizeof(sendBuf), 0);/关闭套接字closesocket(sock);WSACleanup();system(pause);return 0;5.2.5面向连接(TCP)的套接字的系统调用时序图5.3. 基于 UDP 无连接的 socket 编程5.3.1 服务器端操作流程l 加载套接字库(WSAStartup)l 创建套接字 (socket)l 将创建的套接字绑定到一个本地地址和端口上 (bind)l 等待接收数据。后与客户端实现实时交流 (recvfrom / sendto)recvfrom函数将接收一个数据报信息并保存源地址。函数原型如下:int recvfrom(SOCKET s, char FAR* buf, int len, int flags,struct sockaddr FAR* from, int FAR* fromlen);参数:n s:准备接收数据的套接字。n buf:指向缓冲区的指针,该缓冲区用来接收数据。n len:缓冲区的长度。n flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。n from:指向地址的结构体指针,用来接收发送数据方的地址信息。n fromlen:地址结构的大小。sendto函数将向一个特定的目的方放松数据。其函数原型如下:int sendto(SOCKET s, const char FAR* buf, int len, int flags,const struct sockaddr FAR* to, int tolen);参数:n s:是一个(可能已建立连接的)套接字描述符。n buf:指向缓冲区的指针,该缓冲区包含将要发送的数据。n len:指定缓冲区中的数据长度。n flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。n to:该指针指向目标套接字的地址;n tolen:指定的地址长度l 关闭套接字 (closesocket)5.3.2 服务器端代码实现#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);/加载套接字库err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/判断套接字版本号if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)/终止对套接字库的使用WSACleanup();return -2;/创建套接字SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);/定义结构体变量,保存ip和端口号sockaddr_in addr_in;addr_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);/IPaddr_in.sin_family = AF_INET;/地址族addr_in.sin_port = htons(6000);/端口号/绑定套接字bind(sock, (sockaddr*)&addr_in, sizeof(addr_in);/等待并接受数据/定义结构体,保存客户端ip,端口号sockaddr_in addrClient;int len = sizeof(sockaddr_in);char recvBuf1024;recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrClient, &len);cout 接收到的客户端数据: recvBuf endl;/向客户端发送数据char sendBuf = 你好,我是程序猿.;sendto(sock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&addrClient, len);/关闭套接字closesocket(sock);WSACleanup();system(pause);return 0;5.3.3 客户端操作流程l 加载套接字库 (WSAStartup)l 创建套接字 (socket)l 向服务器发送数据。后与服务端实现实时交流 (recvfrom / sendto)l 关闭套接字 (closesocket)5.3.4 客户端代码实现#include #include using namespace std;int main()WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);/加载套接字库err = WSAStartup(wVersion, &wsaData);if (err != 0)return -1;/检查套接字库版本if (LOBYTE(wsaData.wVersion) != 1 | HIBYTE(wsaData.wVersion) != 1)WSACleanup();return -2;/创建套接字SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);/定义结构体变量,保存IP端口号sockaddr_in addrIn;addrIn.sin_addr.S_un.S_addr = inet_addr();addrIn.sin_family = AF_INET;addrIn.sin_port = htons(6000);/发送数据int len = sizeof(addrIn);char sendBuf = 你好,我是峨眉灭绝师太!;sendto(sock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&addrIn, len);/接收数据char recvBuf1024;recvfrom(sock, recvBuf, sizeof(recvBuf), 0, (sockaddr*)&addrIn, &len);cout 接收到服务器数据: recvBuf endl;/关闭套接字closesocket(sock);WSACleanup();system(pause);return 0;5.3.5 无连接协议(UDP)的套接字调用时序图5.4. MFC下的Socket编程5.4.1 CAsyncSocket类l 简介CAsyncSocket 类是在很低的层次上对windows socket API进行了封装,它的成员函数和winsock API的函数调用直接对应,一个CAsyncSocket对象代表了一个windows套接字,它是网络通信的端点。该类将根据不同的windows套接字消息嗲用CAsyncSocket类的回调函数。如果熟悉网络通信细节,仍希望充分利用winsock API编程的灵活性,并能安全的控制程序,同时还希望利用windows对网络事件通知的回调函数的便利,就应该使用CAsyncSocket类进行编程。同时必须自己处理阻塞问题、字节顺序问题和字符串转换问题。CAsyncSocket属于异步非阻塞类。CAsyncSocket类采用了windows socket中的WSAAsyncSelect模型。l 覆盖通知函数 OnAccept 通知侦听套接字,它可以通过调用Accept,接受挂起连接请求 OnClose 通知套接字,关闭对它的套接字连接 OnConnect 通知连接套接字,连接尝试已经完成,无论成功或失败 OnOutOfBandData 通知接收套接字,在套接字上有带外数据读入,通常是忙消息 OnReceive 通知侦听套接字,通过调用Receive恢复数据 OnSend 通知套接字,通过调用Send,它可以发送数据 l 一般编程步骤n 服务器端:CAsyncSocket m_server;/创建套接字if (m_server.Create(m_iPort) = FALSE)MessageBox(Lserver socket create failed);return;/监听套接字if (m_server.Listen() = FALSE)MessageBox(Lserver socket listen failed);return; 至于为什么没有bind,是因为create函数里已经做了bind工作。n 客户端if (m_client.Create() = FALSE)MessageBox(Lclient socket create failed);return;/此处不能通过返回值来判断连接失败,因为此socket是异步的if (m_client.Connect(L,m_iPort) = FALSE)MessageBox(Lclient socket connect failed); return;注意看注释,所以代码不能这样判断是否连接成功。要重载OnConnect,然后在函数中来判断是否连接成功。void CMyAsyncSocket:OnConnect(int nErrorCode)/ TODO: Add your specialized code here and/or call the base classif (nErrorCode = 0)AfxMessageBox(Lconnect success);CAsyncSocket:OnConnect(nErrorCode);l CAsyncSocket编程注意问题:n 阻塞处理。CAsyncSocket对象专用于异步操作,不支持阻塞工作模式,如果应用程序需要支持阻塞操作,必须自己解决。n 字节顺序的转换。在不同的结构类型的计算机之间进行数据传输时,可能会有计算机之间字节存储顺序不一致的情况,需要自己对不用的字节顺序进行转换。n 字符串转换。同样不同结构类型的计算机的字符串顺序也可能不同,需要自行转换。n 在使用CAsyncSocket之前,必须调用AfxSocketInit初始化WinSock环境,而AfxSocketInit会创建一个隐藏的CSocketWnd对象,由于这个对象由Cwnd派生,因此它能够接收Windows消息。一方面它会接受各个CAsyncSocket的状态报告,另一方面它能捕捉系统发出的各种SOCKET事件。l 通信流程5.4.2 CSocket类CSocket类是从CAsyncsocket派生而来的,它继承了CAsyncsocket对WindowsSockets API的封装。与CAsyncsocket对象相比,CSocket对象代表了WindowsSockets API的更高一级的抽象化。CSocket是MFC在CAsyncSocket基础上派生的一个同步阻塞Socket的封装类。5.4.1 常用的函数和注意事项l 常用函数类成员函数功能CSocketCreate初始化,创建套接字CASyncSocketSocket创建socketCASyncSocketSetSockOpt设置套接字选项CASyncSocketBind绑定地址端口CASyncSocketConnect对对等套接字建立连接CASyncSocketListen监听即将到来的连接请求CASyncSocketAccept接受套接字上的连接CASyncSocketSend给连接的套接字发送数据CASyncSocketReceive从连接的套接字接收数据CASyncSocketClose关闭套接字(不等于delete)l 注意事项n 在使用MFC编写socket程序时,必须要包含都文件。n AfxSocketInit() 这个函数,在使用CSocket前一定要先调用该函数,否则使用CSocket会出错;并且该函数还有一个重要的使用方式,就是在某个线程下使用 CSocket 前一定要调用,就算主线程调用了该函数,在子线程下使用 CSocket 也要先调用该函数,要不会出错。n 还要注意的是, Create 方法已经包含了 Bind 方法,如果是以 Create 方法初始化的前提下不能再调用 Bind ,要不一定出错。5.4.2 代码实现设计两个对话框应用程序,通过TCP/IP进行通信,使用MFC的CSocket类实现服务器端和客户端之间的相互通信。服务器服务器端的socket通信需要用到两个socket:1. 用来监听连接的socket2. 用来和客户端通信的socket,此socket中保存了客户端信息因此服务器端需要添加两个继承自CSocket的类,分别起名为CServerSocket、CConnectSocket。l CServerSocket 此socket主要用来监听客户端请求,当有请求到来时,MFC框架将调用OnAccept函数,所以我们需要重写CSocket类的OnAccept函数。/# ServerSocket.h 文件/class CServerSocket : public CSocketpublic:CServerSocket();virtual CServerSocket();void OnAccept(int nErrorCode);/开启socket服务void StartServer(UINT nPort);/发送消息函数void MessageSend(const char* pMesg);private:/保存的是客户端的socket信息CConnectSocket m_clientSock;在OnAccept函数中调用Accept函数,接受客户端请求,另外将客户信息保存到m_clientSock中,使用此套接字对象与客户端进行通信,发送信息可以直接调用API函数Send。在StartServer函数中做了两件事儿,创建套接字,监听套接字。需要主要的是Create函数内部已经对套接字进行了绑定,所以不需要再次绑定。/# ServerSocket.cpp 文件/void CServerSocket:OnAccept(int nErrorCode)Accept(m_clientSock);CSocket:OnAccept(nErrorCode);void CServerSocket:StartServer(UINT nPort)if (!Create(nPort)AfxMessageBox(_T(Socket 创建失败!);return;if (!Listen(5)AfxMessageBox(_T(Socket 监听失败!);return;void CServerSocket:MessageSend(const char* pMesg)m_clientSock.Send(pMesg, strlen(pMesg) + 1);l CConnectSocket类此类中保存了客户端信息,所以用来与客户端进行通

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论