MFC 网络编程 -- 总结
原文链接:http://www.cnblogs.com/lidabo/archive/2012/07/19/2598734.html
1.基于 TCP 的 socket 编程
/*
服务器端程序流程:
1.加载套接字库 WSAStartup
2.创建套接字 socket
3.将我们创建的套接字,绑定到本机地址的某一端口上 bind
4.为套接字设置监听模式,准备客户请求 listen
5.等待客户请求到来。当请求到来,将接受连接请求,并返回一个新的对应于此次连接的套接字 accept
6.用新返回的套接字和客户端进行通信 send / recv
7.在通信结束后,关闭套接字 closesocket 客户端程序流程:
1.加载套接字库 WSAStartup
2.创建套接字 socket
3.向服务器发出请求连接 connect
4.和服务器进行通信 send / recv
5.在通信结束后,关闭套接字 closesocket
*/
服务器端代码:
#include <Winsock2.h>
#include <stdio.h> #pragma comment(lib, "Ws2_32.lib") void main()
{
// 加载套接字库,并进行套接字的版本协商
WORD wVersionRequested; // 指定将要加载的 winsock 库版本
WSADATA wsaData; // 用于存储加载的 winsock 库版本信息
int result; // 用于检测 WSAStartup 函数运行结果 wVersionRequested = MAKEWORD(1, 1); // 设定版本 result = WSAStartup(wVersionRequested, &wsaData); // 函数 WSAStartup 调用成功返回 0
// 出错处理
if (result != 0)
{
return;
} if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return;
} // 创建套接字
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); // 绑定套接字
SOCKADDR_IN addrInfo; // 存储本地主机地址信息 addrInfo.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 本地主机地址
addrInfo.sin_port = htons(6000); // 端口号
addrInfo.sin_family = AF_INET; // 地址族 bind(sock, (SOCKADDR *)&addrInfo, sizeof(SOCKADDR)); // 设置套接字监听模式
listen(sock, 5); SOCKADDR_IN addrInfoClient; // 存储客户端地址信息
int len = sizeof(SOCKADDR); while (true)
{
// 等待客户请求到来,并返回用于通信的套接字
SOCKET sockConnect = accept(sock, (SOCKADDR *)&addrInfoClient, &len); // 下面通过刚建立的套接字,来进行通信 // 发送数据
char sendBuf[100];
sprintf(sendBuf, "这是服务器端,主机地址:%s", inet_ntoa(addrInfo.sin_addr));
send(sockConnect, sendBuf, strlen(sendBuf), 0); // 接收数据
char recvBuf[100];
recv(sockConnect, recvBuf, strlen(recvBuf), 0); // 打印接收的数据
printf("%s\n", recvBuf); closesocket(sockConnect);
} }
客户端代码:
#include <Winsock2.h>
#include <stdio.h> #pragma comment(lib,"Ws2_32.lib") void main()
{
// 加载套接字库,并进行套接字的版本协商
WORD wVersionRequested; // 指定将要加载的 winsock 库版本
WSADATA wsaData; // 用于存储加载的 winsock 库版本信息
int result; // 用于检测 WSAStartup 函数运行结果 wVersionRequested = MAKEWORD(1, 1); // 设定版本 result = WSAStartup(wVersionRequested, &wsaData); // 函数 WSAStartup 调用成功返回 0
// 出错处理
if (result != 0)
{
return;
} if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return;
} // 创建套接字
SOCKET sockConnect = socket(AF_INET, SOCK_STREAM, 0); // 向服务器发出连接请求
SOCKADDR_IN addrInfoServer; // 存储服务器端地址信息
addrInfoServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrInfoServer.sin_port = htons(6000);
addrInfoServer.sin_family = AF_INET; // 向服务器发出连接请求
connect(sockConnect, (SOCKADDR *)&addrInfoServer, sizeof(SOCKADDR)); // 接收数据
char recvBuf[100];
recv(sockConnect, recvBuf, sizeof(recvBuf), 0);
printf("%s\n", recvBuf); // 发送数据
char sendBuf[100] = "这是客户端\n";
send(sockConnect, sendBuf, sizeof(sendBuf) + 1, 0); //关闭套接字
closesocket(sockConnect); WSACleanup(); system("pause");
return;
}
2. 基于 UDP 无连接的 socket 编程
/* 服务端程序流程:
1.加载套接字库 WSAStartup
2.创建套接字 socket
3.将创建的套接字绑定到一个本地地址和端口上 bind
4.等待接收数据。后与客户端实现实时交流 recvfrom / sendto
5.关闭套接字 closesocket 客户端程序流程:
1.加载套接字库 WSAStartup
2.创建套接字 socket
3.向服务器发送数据.后与服务端实现实时交流 recvfrom / sendto
4.关闭套接字 closesocket */
服务器端代码:
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib") void main()
{
// 加载套接字库,并进行套接字的版本协商
WORD wVersionRequested; // 指定将要加载的 winsock 库版本
WSADATA wsaData; // 用于存储加载的 wdnsock 库版本信息
int result; // 用于检测 WSAStartup 函数运行结果 wVersionRequested = MAKEWORD(1, 1); // 设定版本 result = WSAStartup(wVersionRequested, &wsaData); // 函数 WSAStartup 调用成功返回 0
// 出错处理
if (result != 0)
{
return;
} if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return;
} // 创建用于套接字
SOCKET sockConnect = socket(AF_INET, SOCK_DGRAM, 0); // 绑定套接字
SOCKADDR_IN addrInfo; // 存储本地主机地址信息 addrInfo.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // 本地主机地址
addrInfo.sin_port = htons(6000); // 端口号
addrInfo.sin_family = AF_INET; // 地址族 bind(sockConnect, (SOCKADDR *)&addrInfo, sizeof(SOCKADDR)); // 等待接收数据
char recvBuf[100]; // 接收数据缓冲
char sendBuf[100]; // 发送数据缓冲
char tempBuf[200]; SOCKADDR_IN addrInfoClient; // 存储客户端地址信息
int len = sizeof(SOCKADDR); while (true)
{
recvfrom(sockConnect, recvBuf, strlen(recvBuf), 0, (SOCKADDR *)&addrInfoClient, &len);
if ('q' == recvBuf[0])
{
sendto(sockConnect, "q", strlen("q") + 1, 0, (SOCKADDR *)&addrInfoClient, len);
printf("聊天结束");
break;
} sprintf(tempBuf, "%s 说:%s", inet_ntoa(addrInfoClient.sin_addr), recvBuf);
printf("%s\n", tempBuf); // 发送数据
printf("我说:");
gets(sendBuf);
sendto(sockConnect, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrInfoClient, len);
} // 关闭套接字
closesocket(sockConnect);
WSACleanup(); }
客户端代码:
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib") void main()
{
// 加载套接字库,并进行套接字的版本协商
WORD wVersionRequested; // 指定将要加载的 winsock 库版本
WSADATA wsaData; // 用于存储加载的 wdnsock 库版本信息
int result; // 用于检测 WSAStartup 函数运行结果 wVersionRequested = MAKEWORD(1, 1); // 设定版本 result = WSAStartup(wVersionRequested, &wsaData); // 函数 WSAStartup 调用成功返回 0
// 出错处理
if (result != 0)
{
return;
} if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return;
} // 创建套接字
SOCKET sockConnect = socket(AF_INET, SOCK_DGRAM, 0); // 向服务器发送数据
SOCKADDR_IN addrInfoServer; // 存储服务器地址信息 addrInfoServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 指定服务器地址
addrInfoServer.sin_port = htons(6000); // 端口号
addrInfoServer.sin_family = AF_INET; // 地址族 int len = sizeof(SOCKADDR); char recvBuf[100]; // 接收数据缓冲
char sendBuf[100]; // 发送数据缓冲
char tempBuf[200]; while (true)
{
// 发送数据
printf("我说:");
gets(sendBuf);
sendto(sockConnect, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrInfoServer, len); // 等待并接收数据
recvfrom(sockConnect,recvBuf, strlen(recvBuf), 0, (SOCKADDR*)&addrInfoServer, &len);
if ('q' == recvBuf[0])
{
sendto(sockConnect, "q", strlen("q") + 1, 0, (SOCKADDR*)&addrInfoServer, len);
printf("聊天结束");
break;
} sprintf(tempBuf, "%s 说:%s", inet_ntoa(addrInfoServer.sin_addr), recvBuf);
printf("%s\n", tempBuf);
} // 关闭套接字
closesocket(sockConnect);
WSACleanup(); }
3.其他
vc网络编程常用类型解析: 1. SOCKET 类型
SOCKET 是 socket 套接字类型,在 WINSOCK2.H 中有如下定义:
typedef unsigned u_int;
typedef u_int SOCKET;
可知套接字实际上就是一个无符号整形,它将被 Socket 环境管理和使用。
套接字将被创建、设置、用来发送和接收数据,最后会被关闭。 2.WORD 类型、MAKEWORD、LOBYTE、HIBYTE 宏
WORD 类型是一个 16 位的无符号整型, 在 WTYPES.H 中被定义为:
typedef unsigned short WORD;
其目的是提供两个字节的存储, 在 Socket 中这两个字节可以表示主版本号和副版本号。
使用 MAKEWORD 宏可以给一个 WORD 类型赋值。例如要表示主版本号 2, 副版本号 0,可以使用如下代码:
WORD wVersionRequested;
wVersionRequested = MAKEWORD(2, 0);
注意低位内存存储主版本号 2, 高位内存存储副版本号 0,其值为 0x0002。
使用宏 LOBYTE 可以读取 WORD 的低位字节, HIBYTE 可以读取高位字节。 3.WSADATA 类型和 LPWSADATA 类型
WSADATA 类型是一个结构,描述了 Socket 库的一些相关信息,其结构定义如下: typedef struct WSAData
{
WORD wVersion;
WORD wHighVersion;
char szDescription[WSADESCRIPTION_LEN + 1];
char szSystemStatus[WSASYS_STATUS_LEN + 1];
unsigned short iMaxSockets;
unsigned short iMaxUdpDg;
char FAR* lpVendorInfo;
}WSADATA;
typedef WSADATA FAR* LPWSADATA; 值得注意的是 wVersion 字段,存储了 Socket 的版本类型。LPWSADATA 是 WSADATA 的指针类型。
他们通过 Socket 的初始化函数 WSAStartup 读取出来。 //////////////////////////////////////////////////////////////////////////////////////// vc网络编程常用函数解析: 1. WSAStartup 函数
用于初始化 Socket 环境,函数原型:
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
其返回值为整型,调用方式为 PASCAL (即标准类型,PASCAL 等于__stdcall),参数有两个,
第一个参数为 WORD 类型,指明了 Socket 的版本号,第二个参数为 LPWSADATA,指向一个用于存储 Socket 库信息的WSAStartup结构。
返回值:
返回值为0,则初始化成功,若不为0则为失败。 2.WSACleanup 函数
这是 Socket 环境的退出函数,函数原型:
int WSACleanup (void);
返回值:
返回值为0表示成功,SOCKET_ERROR 表示失败。 3.socket 函数
socket 套接字的创建函数,函数原型:
SOCKET socket(int af, int type, int protocol );
第一个参数为:int af, 代表网络地址族,目前只有一种取值有效,即 AF_INET, 代表 internet 地址族;
第二个参数为:int type, 代表网络协议类型, SOCK_DGRAM 代表 UDP 协议, SOCK_STREAM 代表 TCP 协议。
第三个参数为:int protocol,指定网络地址族特殊协议,目前无用,赋值0即可。
返回值:
返回值为 SOCKET, 若返回INVALID_SOCKET 则失败。 4.bind 函数
用于将套接字绑定到一个已知地址上,函数原型:
int bind(SOCKET s, const struct sockaddr FAR *name, int namelen);
第一个参数为:SOCKET s, 指定将被绑定的套接字。
第二个参数为:SOCKADDR_IN *name, 是一个sockaddr结构指针,该结构中包含了要绑定的地址和端口。
第三个参数为:int namelen, 确定第二个参数的结构长度。 返回值: 成功返回0,失败返回SOCKET_ERROR。 /////////////////////////////////////////////////////////////////////////////////////////////////// 下面对其涉及的类型作一番解析:
sockaddr 类型:
sockaddr 类型是用来表示 Socket 地址的类型,同上面的 socketaddr_in 类型相比,sockaddr 的适用范围更广,
因为sockeaddr_in只适用于 TCP/IP 地址。sockaddr 的定义如下:
struct sockaddr
{
ushort sa_family;
char sa_data[14];
};
可知sockaddr 的16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockadddr的。
事实上也往往使用这种方法。 sockaddr_in 定义了socket发送和接收数据包的地址,其定义如下:
strucr sockaddr_in
{
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
}; 其中 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 地址的联合体,有三种表达方式:
第一种用四个字节来表示IP地址的四个数字;
第二种用两个双字节来表示IP地址;
第三种用一个长整型来表示IP地址;
给 in_addr 赋值的一种最简单方法是使用 inet_addr 函数, 它可以把一个代表IP地址的字符串赋值
转换为in_addr类型。如:
addrServer.sin_addr = inet_addr("192.168.0.2");
其反函数是 inet_ntoa,可以把一个 in_addr 类型转换为一个字符串。
sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下:
第一字段 short sin_family,代表网络地址族,如前所述,只能取值AF_INET;
第二字段 u_short sin_port, 代表IP地址端口,由程序员指定;
第三字段 struct in_addr sin_addr, 代表IP地址;
第四个字段char sin_zero[8],是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。 5.listen 函数
该函数让一个套接字在指定IP地址的指定端口处监听连接请求的到来,函数原型:
int listen( SOCKET s, int backlog );
该函数使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。
在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
listen 函数一般在调用bind之后、调用accept之前调用。
返回值: 成功则返回0,失败返回SOCKET_ERROR,可以调用函数WSAGetLastError来取得错误代码。 6.accept函数
该函数从连接请求队列中获得连接信息,并创建新的套接字用于收发数据,实现服务器与客户端的通信。函数原型:
SOCKET accept(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
第一个参数:SOCKET s, 监听套接字
第二个参数:struct sockaddr addr, 存储请求连接的客户端IP地址、端口信息
第三个参数:int addrlen,第二个参数所占空间大小
返回值:
成功返回新套接字,失败返回错误信息 7.connect 函数
向指定的网络主机请求连接,函数原型:
int connect(SOCKET s, const struct sockaddr FAR *name, int namelen);
第一个参数:SOCKET s, 客户端用于收发数据的套接字。
第二个参数:struct sockaddr *name, 指定网络主机IP地址和端口号。
第三个参数:int namelen, 第二参数长度
返回值:
成功返回0,失败返回-1。 8.sendto、recvfrom、send、recv函数
在 Socket 中有两套发送和接收函数。一是sendto 和recvfrom; 二是send 和 recv。
前一套在函数参数中要指明地址(UDP协议),
而后一套需要先将套接字和一个地址绑定,然后直接发送和接收,不需绑定地址。
函数原型:
int sendto( SOCKET s, const char FAR *buf, int len, int flags, const struct sockaddr FAR *to, int tolen);
int recvfrom(SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR *from, int FAR *fromlen); int send(SOCKET s,const char FAR *buf, int len, int flags);
int recv(SOCKET s, char FAR *buf, int len, int flags); 第一个参数: 套接字
第二个参数: 数据指针
第三个参数: 数据长度
第四个参数: 收发数据方式的标识,如果不需要特殊要求可以设置为0,其他值可参考MSDN;
第五个参数: 目标主机地址
第六个参数: 地址的长度 返回值: 运行成功则返回收发数据的字节数,失败返回SOCKET_ERROR 9.closesocket 函数
关闭套接字,函数原型:
int closesocket( SOCKET s );
返回值: 成功返回0,失败返回SOCKET_ERROR。
MFC 网络编程 -- 总结的更多相关文章
- 转:MFC网络编程学习
要学习好网路编程,主要看以下几个方面: 1.掌握概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等. 2.在实际Windows网络通信软件开发中,异步非阻 ...
- 《转》MFC网络编程学习
原地址:http://www.cnblogs.com/renyuan/archive/2013/06/04/3117006.html要学习好网路编程,主要看以下几个方面: 1.掌握概念,诸如:同步(S ...
- MFC 网络编程中::connect返回-1问题
在MFC编写网络时遇到了::connect总是返回-1,但是与服务器可以进行接收和发送消息的操作. 原因是在进行连接的时候我没有进行初始化:::WSAStartup(w, &data);//动 ...
- MFC网络编程
一.概念1.同步方式与异步方式同步方式:发送方不等接收方响应,便接着发送下一个数据包的通信方式异步方式:发送方发出数据,等收到接收方发回的响应后,才发送下一个数据包的通信方式2.阻塞与非阻塞方式阻塞套 ...
- CSocket类网络编程 MFC
Visual C++的MFC提供了CSocket类用来实现网络通信. 下面介绍VC++在Windows 95中实现Socket的 CSocket 类相关成员函数(这些成员函数实际上是从CAsyncSo ...
- VC++ 网络编程总结(一)
1.套接字编程原理 一个完整的网间通信进程需要由两个进程组成,并且只能用同一种高层协议.也就是说,不可能通信的一段用TCP,而另一端用UDP.一个完整的网络信息需要一个五元组来标识:协 ...
- 完毕port(CompletionPort)具体解释 - 手把手教你玩转网络编程系列之三
手把手叫你玩转网络编程系列之三 完毕port(Completion Port)具体解释 ...
- windows socket 网络编程
样例代码就在我的博客中,包含六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具project,和此小工具的全部执行时库.资源和执行程序.代码的压缩包位置是http://www.b ...
- C#网络程序设计(1)网络编程常识与C#常用特性
网络程序设计能够帮我们了解联网应用的底层通信原理! (1)网络编程常识: 1)什么是网络编程 只有主要实现进程(线程)相互通信和基本的网络应用原理性(协议)功能的程序,才能算是真正的网 ...
随机推荐
- Angular系列----AngularJS入门教程05:双向绑定(转载)
在这一步你会增加一个让用户控制手机列表显示顺序的特性.动态排序可以这样实现,添加一个新的模型属性,把它和迭代器集成起来,然后让数据绑定完成剩下的事情. 请重置工作目录: git checkout -f ...
- timestamp的两个属性:CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP
timestamp有两个属性,分别是CURRENT_TIMESTAMP 和ON UPDATE CURRENT_TIMESTAMP两种,使用情况分别如下: 1. CURRENT_TIMESTAMP 当要 ...
- 【Android】记录反编译安卓程序步骤
主要是为了分析一个 App 里面用到的接口,以后移植 UWP 用. 1.http://jd.benow.ca/ 下载 JD-GUI. 2.https://github.com/pxb1988/dex2 ...
- 线段树 + 矩阵 --- ZOJ 3772 Calculate the Function
Calculate the Function Problem's Link: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCod ...
- C#关键字
关键字 abstract as base bool break byte case catch char checked decimal default delegate continue doubl ...
- 使用SQLite数据库和Access数据库的一些经验总结
在我的<Winform开发框架>中,可使用多种数据库作为程序的数据源,除了常规的Oracle数据库.SqlServer.MySql数据库,其中还包括了SQLite数据库.Access数据库 ...
- csharp: Procedure with DAO(Data Access Object) and DAL(Data Access Layer)
sql script code: CREATE TABLE DuCardType ( CardTypeId INT IDENTITY(1,1) PRIMARY KEY, CardTypeName NV ...
- [PE结构分析] 7.相对虚拟地址(RVA)和文件偏移间的转换
RVA是相对虚拟地址(Relative Virtual Address)的缩写.RVA是当PE 文件被装载到内存中后,某个数据位置相对于文件头的偏移量. 例如:导入表的位置和大小可以从PE文件头中IM ...
- Windows程序控件升级==>>构建布局良好的Windows程序
01.菜单栏(MenuStrip) 01.看看这就是menuStrip的魅力: 02.除了一些常用的属性(name.text..)外还有: 03.有人会问:上图的快捷键: 方法: 方式一:1.设置菜单 ...
- PHP redis Api 中文文档
phpredis是php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系 很有用;以下是redis官方提供的命令使用技巧: 下载地址如下: https://github.com/ow ...