现在的位置: 首页 > 综合 > 正文

MFC 网络编程 — 总结

2017年12月03日 ⁄ 综合 ⁄ 共 9511字 ⁄ 字号 评论关闭

基于 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;
}

 

 ================================================

 

 

 基于 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();

}

 

 

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。

 

 

 

 

 

抱歉!评论已关闭.