最简单的Tcp Udp的例子

岁酱吖の 提交于 2020-02-05 11:25:15
//1.WinSock包含2个主要的版本,即WinSock1和WinSock2.前者需要包含头文件WinSock.h和库文件wsock32.lib,后者需要包含头文件WinSock2.h和库文件ws2_32.lib用于提供对网络相关API的支持。

//2.WSAData结构体的定义
    typedef struct WSAData 
    {
        WORD                    wVersion;
        WORD                    wHighVersion;
        #ifdef _WIN64
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        #else
        char                    szDescription[WSADESCRIPTION_LEN+1];
        char                    szSystemStatus[WSASYS_STATUS_LEN+1];
        unsigned short          iMaxSockets;
        unsigned short          iMaxUdpDg;
        char FAR *              lpVendorInfo;
        #endif
    } WSADATA, FAR * LPWSADATA;

//3.MAKEWORD():由两个字节生成一个WORD    //typedef unsigned short      WORD;
    WORD MAKEWORD
    (
        BYTE bLow, //指定新变量的低字节序;
        BYTE bHigh //指定新变量的高字节序;
    );
    WORD temValue = MAKEWORD(0x12, 0x34);    //temValue = 0x3412
    
//4.WSAStartup():
//  返回0为成功
//  WSAStartup应该与WSACleanup成对使用
//  WSAStartup的功能是初始化Winsock DLL
//  本函数必须是应用程序或DLL调用的第一个Windows Sockets函数.
//  它允许应用程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节.
//  应用程序或DLL只能在一次成功的WSAStartup()调用之后才能调用进一步的Windows Sockets API函数.
    
//5.WSACleanup():
//  WSACleanup是来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
//  WSACleanup操作成功返回值为0;否则返回值为SOCKET_ERROR
//  在一个多线程的环境下,WSACleanup中止了Windows Sockets在所有线程上的操作.//  对应于一个任务进行的每一次WSAStartup()调用,必须有一个WSACleanup()调用.只有最后的WSACleanup()做实际的清除工作;前面的调用仅仅将Windows Sockets DLL中的内置引用计数递减
//6.LOWORD()得到一个32bit数的低16bit  
//  HIWORD()得到一个32bit数的高16bit
//  LOBYTE()得到一个16bit数最低(最右边)那个字节
//  HIBYTE()得到一个16bit数最高(最左边)那个字节

//7.网络字节顺序:在低位存储地址中保存数据的高位字节,就是大端字节序。规定不同系统间通信一律采用网络字节序。
//  主机字节顺序:在本机存储时候的保存数据方式。在本机是低位存储地址中保存数据的低位。
//  在进程发送数据之前,都必须将数据转为网络字节顺序。
//  字节序是大端在前还是小端在前是取决于cpu的而不是取决于操作系统的,本机为小端模式

//8.typedef struct sockaddr_in SOCKADDR_IN;
    struct sockaddr_in 
    {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
    };
    typedef struct in_addr
    {
        union 
        {
            struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { USHORT s_w1,s_w2; } S_un_w;
            ULONG S_addr;
        } S_un;
        #define s_addr  S_un.S_addr            // can be used for most tcp & ip code 
        #define s_host  S_un.S_un_b.s_b2    // host on imp
        #define s_net   S_un.S_un_b.s_b1    // network
        #define s_imp   S_un.S_un_w.s_w2    // imp
        #define s_impno S_un.S_un_b.s_b4    // imp #
        #define s_lh    S_un.S_un_b.s_b3    // logical host
    } IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;
//    struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。
//    二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
    typedef struct sockaddr
    {
        #if (_WIN32_WINNT < 0x0600)
        u_short sa_family;
        #else 
        ADDRESS_FAMILY sa_family;           // Address family.
        #endif //(_WIN32_WINNT < 0x0600)

        CHAR sa_data[14];                   // Up to 14 bytes of direct address.
    } SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;


//9.inet_addr():将点分十进制的IP地址转为网络字节序的IP地址
    unsigned long value = inet_addr("127.0.0.1");    //value = 0x0100007f
//  inet_ntoa():将网络字节序的IP地址转为点分十进制的字符串
    in_addr temValue;
    temValue.S_un.S_addr = 0x0100007f;
    char *str = inet_ntoa(temValue);        //str = "127.0.0.1"

//10.主机顺序与网络顺序格式之间的转换
//    htonl()--"Host to Network Long"
//    ntohl()--"Network to Host Long"
//    htons()--"Host to Network Short"
//    ntohs()--"Network to Host Short"

//11.socket():用于创建与指定的服务绑定的套接字
//   socket():第一个参数指明协议族,主要为:
//                    AF_INET:又称PF_INET,是IPv4网络协议的套接字类型
//                    AF_INET6:是IPv6的
//              第二个参数指明套接口类型:
//                    为SOCK_STREAM:提供顺序,可靠,双向和面向连接的字节流数据传输机制,使用TCP
//                    SOCK_DGRAM:支持无连接的数据报,使用UDP
//                    SOCK_RAW:原始套接字,可以用于接收本机网卡上的数据
//              第三个参数一般为0
//   成功时,返回一个小的非负整数值,与文件描述符类似。失败返回INVALID_SOCKET 
    typedef UINT_PTR        SOCKET;
    typedef _W64 unsigned int UINT_PTR, *PUINT_PTR;
    #define INVALID_SOCKET  (SOCKET)(~0)
    #define AF_INET         2               // internetwork: UDP, TCP, etc.
    #define AF_INET6        23              // Internetwork Version 6

//12.bind:把一个本地协议地址赋予一个套接字
//   第一个参数是一个套接字
//   第二个参数是指向SOCKADDR指针
//   第三个参数是SOCKADDR结构体的长度
//   成功返回0,失败返回SOCKET_ERROR
     #define SOCKET_ERROR            (-1)

//13.用listen():创建套接口并为申请进入的连接建立一个后备日志,然后便可用accept()接受连接了。
//   listen()仅适用于支持连接的套接口,如SOCK_STREAM类型的。套接口处于一种“变动”模式,
//   申请进入的连接请求被确认,并排队等待被接受。这个函数特别适用于同时有多个连接请求的服务器;
//   如果当一个连接请求到来时,队列已满,那么客户将收到一个WSAECONNREFUSED错误。
//   如无错误发生,listen()返回0。否则的话,返回SOCKET_ERROR

//14.accept():从已完成连接队列返回下一个已完成连接。如果已完成连接队列为空,那么调用线程将被投入睡眠(假定套接字设为阻塞方式)。
//   如果函数调用成功,则返回一个新建的SOCKET,用于实现服务器和客户端之间的通信,若调用失败,则返回INVALID_SOCKET。

//15.recv():从已连接的SOCKET中接受数据。其第四个参数一般为0。也可以是以下值:
    #define MSG_OOB         0x1             // process out-of-band data  处理带宽外的数据
    #define MSG_PEEK        0x2             // peek at incoming message  数据将赋值到缓冲区中,但是并不从输入队列中删除这些数据
//   调用成功,则返回接受数据的字节数。如果连接已经断开则返回0,否则返回SOCKET_ERROR.

//16.send():可以向已连接的SOCKET上发送数据。其第四个参数一般为0。也可以是以下值:
    #define MSG_OOB         0x1             // process out-of-band data                处理带宽外的数据
    #define MSG_DONTROUTE   0x4             // send without using routing tables    指定数据不选择路由
//   调用成功则返回发送数据长度,可能小于其第三个参数指定的长度。如果出现错误返回SOCKET_ERROR。

//17.closesocket():用于关闭一个SOCKET,释放其所占用的资源。应该在WSACleanup()函数前调用。成功返回0,失败返回SOCKET_ERROR。

//18.shutdown():可以用于禁止在指定的SOCKET上接受和发送数据。当其第二个参数有如下选择
    #define SD_RECEIVE      0x00        //禁止接受数据
    #define SD_SEND         0x01        //禁止发送数据
    #define SD_BOTH         0x02        //禁止接受和发送数据
//   成功返回0,失败返回SOCKET_ERROR

//19.connect():connect()用于建立与指定SOCKET的连接
//   connect在进行连接的时候会默认等待一会,如没有建立连接则返回失败。成功返回0,失败返回SOCKET_ERROR

 

1.TCP服务器

#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;

    if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
    {
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return -1;
    }
    
    SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    if (INVALID_SOCKET == sockfd)    
    {
        WSACleanup();
        return 0;
    }

    SOCKADDR_IN serAddr = {0};
    serAddr.sin_family = AF_INET;            
    serAddr.sin_port = htons(9000);    
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if (SOCKET_ERROR == bind(sockfd, (SOCKADDR*)(&serAddr), sizeof(SOCKADDR)))
    {
        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    if (listen(sockfd, 10) != 0)
    {
        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN addrClient;
    int len = sizeof(SOCKADDR);
    SOCKET clientSocket = accept(sockfd, (SOCKADDR*)&addrClient, &len);

    if (INVALID_SOCKET == clientSocket)
    {
        closesocket(sockfd);
        WSACleanup();
        return -1;
    }

    if (send(clientSocket, "1234", 4, 0) <= 0)
    {
        closesocket(sockfd);
        closesocket(clientSocket);
        WSACleanup();
        return -1;
    }

    char buff[1024] = {0};

    if (recv(clientSocket, buff, 4, 0) <= 0)
    {
        closesocket(sockfd);
        closesocket(clientSocket);
        WSACleanup();
        return -1;
    }

    printf("%s\n", buff);
    system("pause");

    closesocket(sockfd);
    closesocket(clientSocket);
    //注意点:所有打开的SOCKET都需要进行关闭

    WSACleanup();
    return 0;
}

 

2.TCP客户端

#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return -1;
    }
    
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return -1;
    }

    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
    if (INVALID_SOCKET == sockClient)
    {
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN sockAddr = {0};
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(9000);
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if (SOCKET_ERROR == connect(sockClient, (SOCKADDR*)&sockAddr, sizeof SOCKADDR))
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }

    char buff[1024] = {0};
    if (recv(sockClient, buff, 4, 0) <= 0)
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }
    printf("%s\n", buff);

    if (send(sockClient, "1234", 4, 0) <= 0)
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }
    system("pause");

    closesocket(sockClient);
    WSACleanup();
    return 0;
}

 

3.Udp发送方

//1.sendto():
//  第一个参数:用于发送数据的套接字
//  第二个参数:发送缓冲区
//  第三个参数:发送长度
//  第四个参数:标志位。一般为0。可以为MSG_DONTROUTE和MSG_OOB。(效果在send()中使用一致)
//  第五个参数:指定数据接收方的地址
//  第六个参数:第五个参数的数据长度

#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return -1;
    }

    SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
    if (INVALID_SOCKET == sockClient)
    {
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(9000);
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    int sendLength = sendto(sockClient, "123", 3, 0, (SOCKADDR*)&sockAddr, sizeof SOCKADDR);
    if (sendLength <= 0)
    {
        closesocket(sockClient);
        WSACleanup();
        return -1;
    }

    closesocket(sockClient);
    WSACleanup();
    return 0;
}

4.Udp接收方

//1.recvfrom():
//  第一个参数:用于监听的套接字
//  第二个参数:接受缓冲区
//  第三个参数:接受长度
//  第四个参数:调用方式。一般为0。可以为MSG_PEEK,MSG_OOB。(效果和在recv()中使用一致)
//  第五个参数:用于保存发送方地址
//  第六个参数:第五个参数长度

#include "stdafx.h"
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        return -1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        WSACleanup();
        return -1;
    }

    SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
    if (INVALID_SOCKET == sockSrv)
    {
        WSACleanup();
        return -1;
    }

    SOCKADDR_IN sockAddr;
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = htons(9000);
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    if (SOCKET_ERROR == bind(sockSrv, (SOCKADDR*)&sockAddr, sizeof SOCKADDR))
    {
        closesocket(sockSrv);
        WSACleanup();
    }

    char buff[1024] = {};

    int len = sizeof SOCKADDR;
    SOCKADDR_IN ClientAddr = {0};
    int recvLength = recvfrom(sockSrv, buff, 1023, 0, (SOCKADDR*)&ClientAddr, &len);
    
    closesocket(sockSrv);
    WSACleanup();
    return 0;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!