千家信息网

C++ Socket如何实现TCP与UDP网络编程

发表于:2024-11-24 作者:千家信息网编辑
千家信息网最后更新 2024年11月24日,这篇文章将为大家详细讲解有关C++ Socket如何实现TCP与UDP网络编程,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言socket编程分为TCP和UDP两
千家信息网最后更新 2024年11月24日C++ Socket如何实现TCP与UDP网络编程

这篇文章将为大家详细讲解有关C++ Socket如何实现TCP与UDP网络编程,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    前言

    socket编程分为TCP和UDP两个模块,其中TCP是可靠的、安全的,常用于发送文件等,而UDP是不可靠的、不安全的,常用作视频通话等。

    如下图:

    头文件与库:

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

    准备工作:

    创建工程后,首先右键工程,选择属性

    然后选择 C/C++ - 预处理器 - 预处理器定义

    将字符串 _WINSOCK_DEPRECATED_NO_WARNINGS 添加到里面去,点击应用即可!

    TCP

    连接过程图:

    创建tcp服务器和客户端都是按照上图的步骤来操作的!

    1). 服务器

    初始化套接字库
    对应图中socket()

    WORD wVersion;WSADATA wsaData;int err;// 设置版本,可以理解为1.1wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来// 启动err = WSAStartup(wVersion, &wsaData);

    创建tcp套接字
    对应图中socket()

    // AF_INET:ipv4   AF_INET6:ipv6SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

    绑定到本机
    对应图中bind()

    // 准备绑定信息SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);     // 设置绑定网卡addrSrv.sin_family = AF_INET;         // 设置绑定网络模式addrSrv.sin_port = htons(6000);               // 设置绑定端口// hton: host to network  x86:小端    网络传输:htons大端// 绑定到本机int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

    监听
    对应图中listen()

    // 同时能接收10个链接,主要看参数二的设置个数listen(sockSrv, 10);

    接收连接请求,返回针对客户端的套接字
    对应图中accept()

    SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);

    发送数据
    对应图中write()

    sprintf_s(sendBuf, "hello client!\n");int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);

    接收数据
    对应图中read()

    recv(sockConn, recvBuf, 100, 0);

    关闭套接字
    对应图中close()

    closesocket(sockConn);

    清理套接字库

    WSACleanup();

    具体实现代码

    #include #include #include #pragma comment(lib, "ws2_32.lib")int main(void) {        // 1.初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        // 设置版本,可以理解为1.1        wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来        // 启动        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        // 检查:网络低位不等于1 || 网络高位不等于1        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                // 清理套接字库                WSACleanup();                return -1;        }        // 2.创建tcp套接字              // AF_INET:ipv4   AF_INET6:ipv6        SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);        // 准备绑定信息        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);     // 设置绑定网卡        addrSrv.sin_family = AF_INET;         // 设置绑定网络模式        addrSrv.sin_port = htons(6000);               // 设置绑定端口        // hton: host to network  x86:小端    网络传输:htons大端        // 3.绑定到本机        int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));        if (retVal == SOCKET_ERROR) {                printf("Failed bind:%d\n", WSAGetLastError());                return -1;        }        // 4.监听,同时能接收10个链接        if (listen(sockSrv, 10) == SOCKET_ERROR) {                printf("Listen failed:%d", WSAGetLastError());                return -1;        }        std::cout << "Server start at port: 6000" << std::endl;        SOCKADDR_IN addrCli;        int len = sizeof(SOCKADDR);        char recvBuf[100];        char sendBuf[100];        while (1) {                // 5.接收连接请求,返回针对客户端的套接字                SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);                if (sockConn == SOCKET_ERROR) {                        //printf("Accept failed:%d", WSAGetLastError());                        std::cout << "Accept failed: " << WSAGetLastError() << std::endl;                        break;                }                //printf("Accept client IP:[%s]\n", inet_ntoa(addrCli.sin_addr));                std::cout << "Accept client IP: " << inet_ntoa(addrCli.sin_addr) << std::endl;                // 6.发送数据                sprintf_s(sendBuf, "hello client!\n");                int iSend = send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);                if (iSend == SOCKET_ERROR) {                        std::cout << "send failed!\n";                        break;                }                // 7.接收数据                recv(sockConn, recvBuf, 100, 0);                std::cout << recvBuf << std::endl;                // 关闭套接字                closesocket(sockConn);        }        // 8.关闭套接字        closesocket(sockSrv);        // 9.清理套接字库        WSACleanup();        return 0;}

    2). 客户端

    初始化套接字库
    对应图中socket()

    WORD wVersion;WSADATA wsaData;int err;// 可以理解为1.1wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来// 启动err = WSAStartup(wVersion, &wsaData);// 创建TCP套接字SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);

    连接服务器
    对应图中connect()

    // 连接服务器int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

    发送数据到服务器
    对应图中write()

    char sendBuf[] = "你好,服务器,我是客户端!";send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);

    接收服务器的数据
    对应图中read()

    char recvBuf[100];recv(sockCli, recvBuf, sizeof(recvBuf), 0);

    关闭套接字并清除套接字库
    对应图中close()

    closesocket(sockCli);WSACleanup();

    具体实现代码

    #include #include #pragma comment(lib, "ws2_32.lib")int main(void) {        // 1.初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        // 可以理解为1.1        wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来        // 启动        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        // 检查:网络地位不等于1 || 网络高位不等于1        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                // 清理套接字库                WSACleanup();                return -1;        }        // 创建TCP套接字        SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");      // 服务器地址        addrSrv.sin_port = htons(6000);               // 端口号        addrSrv.sin_family = AF_INET;         // 地址类型(ipv4)        // 2.连接服务器        int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));        if (err_log == 0) {                printf("连接服务器成功!\n");                } else {                printf("连接服务器失败!\n");                return -1;        }        char recvBuf[100];        char sendBuf[] = "你好,服务器,我是客户端!";        // 3.发送数据到服务器        send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);        // 4.接收服务器的数据        recv(sockCli, recvBuf, sizeof(recvBuf), 0);        std::cout << recvBuf << std::endl;        // 5.关闭套接字并清除套接字库        closesocket(sockCli);        WSACleanup();        system("pause");        return 0;}

    运行效果:

    3). TCP聊天小项目

    下面是根据上面的代码修改的一个聊天小项目(使用到了多线程)

    只有一个服务器,服务器一直开启等待客户端连接;
    客户都安可以开启多个,且可以一直连续的与服务器进行发送接收消息;
    服务器给客户端发送数据,得通过1 - 9来区分到底给那个客户端发送消息,例如给第二个客户端发送消息:2你好,客户端
    客户端那边接收到的数据是:你好,客户端

    服务器代码

    #include #include #include #include #include #include #include #include #include #pragma comment(lib, "ws2_32.lib")SOCKET sockSrv;std::vector vec_sockConn;std::vector vec_sockaddr_in;std::vector vec_sockIndex;// 这个结构体用作线程参数typedef struct SERVER_CLIENT {        SOCKET server;        SOCKADDR_IN client;        int clientIndex;}SC;// 判断有没有断开连接bool IsSocketClosed(SOCKET clientSocket) {        bool ret = false;        HANDLE closeEvent = WSACreateEvent();        WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);        DWORD dwRet = WaitForSingleObject(closeEvent, 0);        if (dwRet == WSA_WAIT_EVENT_0)                ret = true;        else if (dwRet == WSA_WAIT_TIMEOUT)                ret = false;        WSACloseEvent(closeEvent);        return ret;}// 接收请求unsigned int WINAPI  ThreadAccept(LPVOID p) {        static int i = 0;        while (1) {                SOCKADDR_IN addrCli;                int len = sizeof(SOCKADDR);                // 5.接收连接请求,返回针对客户端的套接字                SOCKET sockConn = accept(sockSrv, (SOCKADDR *)&addrCli, &len);                if (sockConn == SOCKET_ERROR) {                        printf("Accept failed:%d", WSAGetLastError());                }                // 存储当前服务器与客户端 连接绑定的socket                vec_sockIndex.emplace_back(i++);                vec_sockaddr_in.emplace_back(addrCli);                vec_sockConn.emplace_back(sockConn);                printf("\033[0;%d;40m客户端[%d]上线\033[0m\n", 31, i);        }                return 0;}unsigned int WINAPI  _ThreadRecv(LPVOID p) {        char recvBuf[100];        memset(recvBuf, 0, 100);        SC _sc = *(SC *)p;                while (1) {                Sleep(20);                if (IsSocketClosed(_sc.server) == true) {                        printf("客户端 [%d] 断开连接!\n", _sc.clientIndex + 1);                        break;                }                // 接收数据                recv(_sc.server, recvBuf, 100, 0);                if (strlen(recvBuf) == 0) {                        continue;                }                        printf("接收到客户端 [%d] 的消息:%s\n", _sc.clientIndex + 1, recvBuf);                memset(recvBuf, 0, 100);        }        return 0;}unsigned int WINAPI  ThreadRecv(LPVOID p) {        static int index = 0;        while (1) {                // 还没有客户端与服务器进行连接                if (vec_sockConn.size() == 0) {                        continue;                }                // 接收线程已经开启和客户端个数相等                if (vec_sockConn.size()  == index) {                        continue;                }                SC sc;                sc.server = vec_sockConn.at(index);                sc.client = vec_sockaddr_in.at(index);                sc.clientIndex = vec_sockIndex.at(index);                HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadRecv, (void *)&sc, 0, NULL);                index++;                Sleep(20);        }        return 0;}int main(void) {        // 1.初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        // 设置版本,可以理解为1.1        wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来        // 启动        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        // 检查:网络低位不等于1 || 网络高位不等于1        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                // 清理套接字库                WSACleanup();                return -1;        }        // 2.创建tcp套接字              // AF_INET:ipv4   AF_INET6:ipv6        sockSrv = socket(AF_INET, SOCK_STREAM, 0);        // 准备绑定信息        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);     // 设置绑定网卡        addrSrv.sin_family = AF_INET;         // 设置绑定网络模式        addrSrv.sin_port = htons(6000);               // 设置绑定端口        // hton: host to network  x86:小端    网络传输:htons大端        // 3.绑定到本机        int retVal = bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));        if (retVal == SOCKET_ERROR) {                printf("Failed bind:%d\n", WSAGetLastError());                return -1;        }        // 4.监听,同时接收10个链接        if (listen(sockSrv, 10) == SOCKET_ERROR) {                printf("Listen failed:%d", WSAGetLastError());                return -1;        }        std::cout << "Server start at port: 6000" << std::endl;        // 线程句柄                            // 创建线程        HANDLE hThread_1 = (HANDLE)_beginthreadex(NULL, 0, ThreadAccept, NULL, 0, NULL);        HANDLE hThread_2 = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);        //uiInit();        //editPrint(0, ">");        char sendBuf[100];        while (1) {                //printf("请输入发送内容:");                char c = getchar();                              // 输入发送给谁                scanf_s("%s", sendBuf, 100);        // 输入发送的内容                if (strlen(sendBuf) == 0) {                        printf("输入内容为空或者超长!\n");                }                // 1 至 9                if (c < '1' || c > '9' || vec_sockConn.size() == 0 || c - '0' >= vec_sockConn.size() + 1) {                        while ((c = getchar()) != '\n');       // 清空输入缓冲区                        memset(sendBuf, 0, 100);                        printf("输入内容不符合规则!\n");                        continue;                }                // 发送数据                int index = --c - '0'; // 因为下标是从零开始的,所以c要先自减                int iSend = send(vec_sockConn.at(index) , sendBuf, strlen(sendBuf) + 1, 0);                if (iSend == SOCKET_ERROR) {                        std::cout << "send failed!\n";                        break;                }                memset(sendBuf, 0, 100);                while ((c = getchar()) != '\n');       // 清空输入缓冲区        }        // 关闭套接字        std::vector::iterator it = vec_sockConn.begin();        for (; it != vec_sockConn.end(); it++) {                closesocket((SOCKET)(*it));        }        WaitForSingleObject(hThread_1, INFINITE);        WaitForSingleObject(hThread_2, INFINITE);        CloseHandle(hThread_1);        CloseHandle(hThread_2);        // 7.关闭套接字        closesocket(sockSrv);        // 8.清理套接字库        WSACleanup();        return 0;}

    客户端

    #include #include #include #include #pragma comment(lib, "ws2_32.lib")SOCKET sockCli;// 判断有没有断开连接bool IsSocketClosed(SOCKET clientSocket) {        bool ret = false;        HANDLE closeEvent = WSACreateEvent();        WSAEventSelect(clientSocket, closeEvent, FD_CLOSE);        DWORD dwRet = WaitForSingleObject(closeEvent, 0);        if (dwRet == WSA_WAIT_EVENT_0)                ret = true;        else if (dwRet == WSA_WAIT_TIMEOUT)                ret = false;        WSACloseEvent(closeEvent);        return ret;}unsigned int WINAPI  ThreadRecv(LPVOID p) {        char recvBuf[100];        memset(recvBuf, 0, 100);        while (1) {                Sleep(20);                if (IsSocketClosed(sockCli) == true) {                        printf("服务器 断开连接!\n");                        break;                }                // 接收服务器的数据                recv(sockCli, recvBuf, sizeof(recvBuf), 0);                if (strlen(recvBuf) == 0) continue;                std::cout << recvBuf << std::endl;                memset(recvBuf, 0, 100);        }        return 0;}int main(void) {        // 1.初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        // 可以理解为1.1        wVersion = MAKEWORD(1, 1);       // 例:MAKEWORD(a, b) --> b | a << 8 将a左移8位变成高位与b合并起来        // 启动        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        // 检查:网络地位不等于1 || 网络高位不等于1        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                // 清理套接字库                WSACleanup();                return -1;        }        // 创建TCP套接字        sockCli = socket(AF_INET, SOCK_STREAM, 0);        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");      // 服务器地址        addrSrv.sin_port = htons(6000);               // 端口号        addrSrv.sin_family = AF_INET;         // 地址类型(ipv4)        // 连接服务器        int err_log = connect(sockCli, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));        if (err_log == 0) {                printf("连接服务器成功!\n");        } else {                printf("连接服务器失败!\n");                return -1;        }        // 线程句柄                            // 创建线程        HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, NULL, 0, NULL);        char sendBuf[100];        while (1) {                //printf("请输入发送内容:");                scanf_s("%s", sendBuf, 100);                // 发送数据到服务器                send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);                memset(sendBuf, 0, 100);                char c;                while ((c = getchar()) != '\n');        }        WaitForSingleObject(hThread, INFINITE);        CloseHandle(hThread);        // 关闭套接字并清除套接字库        closesocket(sockCli);        WSACleanup();        system("pause");        return 0;}

    运行效果:

    UDP

    UDP就比较简单了,步骤比tcp要少一些。

    连接过程图:

    1). 服务器

    初始化套接字库

    WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);

    创建套接字

    SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);

    绑定

    // SOCKADDR_IN addrSrv; 省略了定义和赋值bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));

    接收数据

    char recvBuf[100];recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);

    发送数据

    char sendBuf[] = "hello Client,I'm Server!\n";sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);

    关闭

    closesocket(sockSrv);WSACleanup();

    具体实现代码

    #include #include #pragma comment(lib, "ws2_32.lib")int main(void) {        // 初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        wVersion = MAKEWORD(1, 1);        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                WSACleanup();                return -1;        }        // 创建套接字        SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);        addrSrv.sin_family = AF_INET;        addrSrv.sin_port = htons(6001);        // 绑定到本机6001端口        bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));        // 接收请求,处理请求        SOCKADDR_IN addrCli;        int len = sizeof(SOCKADDR);        char sendBuf[] = "hello Client,I'm Server!\n";        char recvBuf[100];        std::cout << "start UDP server with port 6001" << std::endl;        while (1) {                // 接收数据                recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);                std::cout << "Recv:" << recvBuf << std::endl;                // 发送数据                sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrCli, len);                std::cout << "Send:" << sendBuf << std::endl;        }        closesocket(sockSrv);        WSACleanup();        return 0;}

    2). 客户端

    初始化套接字库

    WORD wVersion;WSADATA wsaData;int err;wVersion = MAKEWORD(1, 1);

    创建UDP套接字

    SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);SOCKADDR_IN addrSrv;

    接收数据

    char recvBuf[100];recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);

    发送数据

    char sendBuf[] = "hello Client,I'm Server!\n";sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);

    关闭

    closesocket(sockSrv);WSACleanup();

    具体实现代码

    #include #include #pragma comment(lib, "ws2_32.lib")int main(void) {        // 初始化套接字库        WORD wVersion;        WSADATA wsaData;        int err;        wVersion = MAKEWORD(1, 1);        err = WSAStartup(wVersion, &wsaData);        if (err != 0) {                return err;        }        if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) {                WSACleanup();                return -1;        }        // 创建UDP套接字        SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);        SOCKADDR_IN addrSrv;        addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");        addrSrv.sin_family = AF_INET;        addrSrv.sin_port = htons(6001);        SOCKADDR_IN addrCli;        int len = sizeof(SOCKADDR);        char sendBuf[] = "hello, I'm Client!\n";        char recvBuf[100];        std::cout << "send to Server: " << sendBuf << std::endl;        sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR *)&addrSrv, len);        recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR *)&addrCli, &len);        std::cout << "recv from: " << recvBuf << std::endl;        closesocket(sockCli);        WSACleanup();        system("pause");        return 0;}

    运行效果:

    关于"C++ Socket如何实现TCP与UDP网络编程"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

    0