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,失败返回-18.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。
posted @ 2016-08-05 13:30  wuyuan2011woaini  阅读(376)  评论(0编辑  收藏  举报