C++编程笔记(通信)(win32平台)

2023-10-31

一、初始化网络库

基本都得这么些

bool InitSocket() {//可以直接写到mian中
    WSADATA *wsadata = new WSADATA();
    if (0 != WSAStartup(MAKEWORD(2, 2), wsadata)) {
        ERROR("WSADATA");
        return false;
    };
    return true;
}
InitSocket();
SetConsoleOutputCP(65001);//防止乱码,因为我用clion调用命令行会乱码

二、socket套接字

2.1服务端

//用于输出错误信息
#define ERROR(errMsg) std::cout<< "[error] "<<errMsg<<" failed code:" << WSAGetLastError()<<"\tline:"<<__LINE__<<"\n";

//创建一个空socket
SOCKET socket1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket1) {
    ERROR("SOCKET");
    return INVALID_SOCKET;
}
struct sockaddr_in addrClient;//用来放连接者的ip信息
//绑定ip+port
struct sockaddr_in *addr = new struct sockaddr_in;
addr->sin_family = AF_INET;
addr->sin_port = htons(8888);
addr->sin_addr.S_un.S_addr = ADDR_ANY;

if (SOCKET_ERROR == bind(socket1, reinterpret_cast<const sockaddr *>(addr), sizeof(*addr))) {
    ERROR("bind");
    return INVALID_SOCKET;
}
delete addr;
listen(socket1, 20);		//最大连接数

clientSocket = accept(serverSocket, (struct sockaddr *) &addrClient, socket_len);//接受连接,并返回客户端socket,通过这个socket与客户端通信


2.2客户端

SOCKET socket1 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket1) {
    ERROR("SOCKET");
    return INVALID_SOCKET;
}
//绑定服务器的ip+port
struct sockaddr_in *addr = new struct sockaddr_in;
addr->sin_family = AF_INET;
addr->sin_port = htons(8888);
addr->sin_addr.S_un.S_addr = inet_addr(ip);//绑定服务器ip("127.0.0.1")  字符串类型
if (INVALID_SOCKET == connect(socket1, reinterpret_cast<const sockaddr *>(addr), sizeof(*addr))) {
    ERROR("connect");
    delete addr;
    return INVALID_SOCKET;
}
delete addr;
return socket1;//通过这个socket就可以与服务器通信了

三、发送、接收数据

3.1发送

/*
*  参数一: 套接字,要往哪里发数据就写那个套接字
*  参数二: 发送的数据
*  参数三: 发送的数据的长度
*  参数四: flag  我也不知道有啥用,反正无脑填0没问题
*/
char *buf = new char[1024];
memset(buf, 0, sizeof(buf));//清理内存,建议往里面填东西之前先清空
send(clientSocket, buf, strlen(buf), 0);

3.2接收数据

/*recv函数(阻塞函数) 
* 参数一: 发送者的socket
* 参数二: 接受数据的缓冲区(用之前一定要清空)
* 参数三: buf的长度
* 参数四: flag
* 返回值: 接收到的数据长度,若为0,则说明连接断开
*/
char *buf = new char[1024];
memset(buf, 0, sizeof(*buf));//接受数据之前一定要清空,一定要清空,一定要清空,一定要清空
int ret2 = recv(this->Socket, buf, 1024, 0);
if (ret2 <= 0) {
    cout << "id: " << this->Socket << "  disconnect!\n";
    closesocket(this->Socket);
    return;
} else {
    cout << "recv:\t" << buf << endl;
}

四、自定义的结构体

4.1 发送端

typedef struct user {
    char userName[32];
    char password[32];
} USER;

char *buf = new char[1024];
USER *login = new USER();
memset(buf, 0, sizeof(buf));		//将内存置空
memcpy(buf, login, sizeof(USER));	//将结构体数据拷贝到内存发送缓冲区
send(clientSocket, buf, sizeof(*login), 0);

4.2接收端

char *buf = new char[1024];
USER *user = new USER();
memset(buf, '\0', 1024);
memset(user, 0, sizeof(USER));
ret1 = recv(this->Socket, buf, sizeof(USER), 0);
memcpy(user, buf, sizeof(*user));//也可以事先判断一下是否接收成功
/*
* ret返回值
* 大于0: 接收到的字节数
* 等于0: 连接断开
* 小于0: 出错,一般就是套接字关闭了
*/

IPV6版本套接字的创建

//server.cpp
#include <iostream>
#include <string>
#include "windows.h"
#include <WinSock2.h>
#include <WS2tcpip.h>

int main(int argc, char *argv[]) {
	/*
	首先初始化网络库,这里就不写出来了
	*/
    if (argc < 2) {
        cout << "param error ,you should give port\n exmaple: server.exe 9999" << endl;
        exit(-1);
    }
    SetConsoleOutputCP(65001);//避免控制台乱码
    int listenfd = 0;
    const char on = 1;
    struct addrinfo hints{0}, *res, *ressave;
    hints.ai_flags = AI_PASSIVE;//被动匹配所有ip包括ipv6,通常用于bind
    hints.ai_family = AF_UNSPEC;//允许ipv4或者ipv6
    hints.ai_socktype = SOCK_STREAM;//流类型
    hints.ai_protocol = IPPROTO_IP;//匹配所有ip协议
    char mIpAddr[16];//ipv6 128位

    //获取ip地址,res指向一个包含ip信息的链表
    //param1:IP地址,null表示所有ip;param2:端口;param3:参数设置,上述有详解;param4返回值,返回包含端口,ip信息的一个链表
    //使用getaddrinfo获取本地所有的ip包括可以使用域名作为参数
    auto ret = getaddrinfo(nullptr, argv[1], &hints, &res);//nullptr表示本机所有ip和别名
    if (ret == -1) {
        perror("getaddrinfo");
        exit(ret);
    }
    ressave = res;

    while (res != nullptr) {
        //创建一个监听套接字
        if (-1 == (listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol))) {
            perror("socket");
            res = res->ai_next;
            continue;
        }

        //设置端口复用
        if (-1 == setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
            cout << "setsockopt error: " << strerror(errno) << endl;
            closesocket(listenfd);
            res = res->ai_next;
            continue;
        }
        int ipv6only = 0;//将ipv6only设置为0,这样两个版本的ip都能用
        if (setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &ipv6only, sizeof(ipv6only)) != 0) {
            cout << "set ipv6only failed!";
            continue;
        }
        //绑定
        if (SOCKET_ERROR == bind(listenfd, res->ai_addr, res->ai_addrlen)) {
            closesocket(listenfd);
            perror("bind");
            res = res->ai_next;
            continue;
        }
        //监听
        if (-1 == listen(listenfd, 10)) {
            closesocket(listenfd);
            perror("listen");
            res = res->ai_next;
            continue;
        }
        break;
    }

    freeaddrinfo(ressave);
    ressave = nullptr;
    res = nullptr;

    if (listenfd < 0) {
        perror("listenfd");
        exit(-1);
    }
    struct sockaddr_storage clientAddr{0};//通用结构体
    int len = sizeof(clientAddr);

    //等待链接
    int clientfd = accept(listenfd, (struct sockaddr *) &clientAddr, &len);
    auto tmpaddr = new char[32];
    DWORD tmpaddrlen=INET6_ADDRSTRLEN;
    //sockaddr *addr = (struct sockaddr *) &clientAddr;
    if (clientAddr.ss_family == AF_INET6) {
        struct sockaddr_in6 *p = (struct sockaddr_in6 *) &clientAddr;
        printf("16进制ip地址为:");
        WSAAddressToStringA((LPSOCKADDR) p, sizeof(struct sockaddr_in6), nullptr, (LPSTR) tmpaddr, &tmpaddrlen);
        cout << tmpaddr << endl;
    } else if (clientAddr.ss_family == AF_INET) {
        struct sockaddr_in *p = (struct sockaddr_in *) &clientAddr;
        cout << "client ip:" << inet_ntoa(p->sin_addr);
    } else {
        perror("获取客户端信息失败");
        exit(-1);
    }
    //至此连接已经建立,就可以通过,可以使用clientfd进行通信了

}

客户端口

//client.cpp
//
// Created by lhh on 2022/4/16.
//
#include <iostream>
#include <string>
#include "windows.h"
#include <WinSock2.h>
#include <WS2tcpip.h>//getaddrinfo inet_ntop

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

using namespace std;

int main(int argc, char *argv[]) {
    if (argc < 3) {
        cout << "param error ,you should give ip and port\n exmaple: server.exe 127.0.0.1 9999" << endl;
        exit(-1);
    }
    SetConsoleOutputCP(65001);
    int sockfd = 0;
    const char on = 1;
    struct addrinfo hints{0}, *res, *ressave;
    hints.ai_flags = AI_PASSIVE;//匹配所有ip
    hints.ai_family = AF_UNSPEC;//允许ipv4或者ipv6
    hints.ai_socktype = SOCK_STREAM;//流类型
    hints.ai_protocol = IPPROTO_IP;//匹配所有协议
    char mIpAddr[16];//ipv6 128位

    //获取ip地址,res指向一个包含ip信息的链表
    auto ret = getaddrinfo(argv[1], argv[2], &hints, &res);
    if (ret == -1) {
        perror("getaddrinfo");
        exit(ret);
    }
    ressave = res;
    char *tmpaddr = new char[50];
    memset(tmpaddr,0,50);
    DWORD tmpaddrlen=INET6_ADDRSTRLEN;
    //查看获取到的ip地址
    for (auto cur = res; cur != nullptr; cur = cur->ai_next) {
        if (cur->ai_family == AF_INET) {
            auto addr = (sockaddr_in *) cur->ai_addr;//解析出ip的地址
            sprintf(mIpAddr, "IPV4:%d.%d.%d.%d", addr->sin_addr.S_un.S_un_b.s_b1,
                    addr->sin_addr.S_un.S_un_b.s_b2, addr->sin_addr.S_un.S_un_b.s_b3, addr->sin_addr.S_un.S_un_b.s_b4);
            printf("%s\n", mIpAddr);
        } else if (cur->ai_family == AF_INET6) {
            struct sockaddr_in6 *p = (struct sockaddr_in6 *) (cur->ai_addr);
            WSAAddressToStringA((LPSOCKADDR) cur->ai_addr, sizeof(struct sockaddr_in6), nullptr, (LPSTR) tmpaddr, &tmpaddrlen);
            printf("IPV6:%s",tmpaddr);
        }
    }

    while (res != nullptr) {//由于获取到的ip不一定都能访问到,所以循环读取链表,读取到可以成功连接的就跳出循环
        //创建一个套接字
        if (-1 == (sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol))) {
            perror("socket");
            res = res->ai_next;
            continue;
        }
        //建立链接
        if (-1 == connect(sockfd, res->ai_addr, res->ai_addrlen)) {
            perror("connect");
            closesocket(sockfd);
            res = res->ai_next;
            continue;
        }
        printf("connect success");
        break;
    }
    freeaddrinfo(ressave);
    ressave = nullptr;
    res = nullptr;
    //后续通过套接字通信就可以了

}


#ifdef WIN32

class WSInit {
public:
    WSInit() {
        WSADATA wsadata;
        WSAStartup(MAKEWORD(2, 2), &wsadata);
    }

    ~WSInit() { WSACleanup(); }
};

static WSInit wsinit_;
#endif

参考:

IPV6相关函数解析:https://blog.csdn.net/v6543210/article/details/106927210

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++编程笔记(通信)(win32平台) 的相关文章

  • 为什么相同的代码在同一台计算机上的执行时间可能不同?

    我是 C 编程新手 我编写了代码并希望获得它的运行时 这就是我所做的 每次运行代码时 我都会得到不同的运行时值 这样对吗 或者我的代码有问题吗 int main int argc char argv time t start end sta
  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • 如何使用MemoryCache代替Timer来触发一个方法?

    以下方法通过等待已运行操作的结果来处理并发请求 对数据的请求可能会使用相同 不同的凭据同时出现 对于每组唯一的凭据 最多可以有一个GetCurrentInternal呼叫正在进行中 当准备就绪时 该呼叫的结果将返回给所有排队的服务员 pri
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 读取文件特定行号的有效方法。 (奖励:Python 手册印刷错误)

    我有一个 100 GB 的文本文件 它是来自数据库的 BCP 转储 当我尝试导入它时BULK INSERT 我在第 219506324 行上收到一个神秘错误 在解决此问题之前 我想看看这一行 但可惜的是我最喜欢的方法 import line
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • ASP.NET:获取自 1970 年 1 月 1 日以来的毫秒数

    我有一个 ASP NET VB NET 日期 我试图获取自 1970 年 1 月 1 日以来的毫秒数 我尝试在 MSDN 中寻找方法 但找不到任何东西 有谁知道如何做到这一点 从 NET 4 6 开始 该方法ToUnixTimeMillis
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 如何将自定义 JSON 文件添加到 IConfiguration 中?

    我正在使用 asp net Autofac 我正在尝试加载自定义 JSON 配置文件 并基于该文件创建 实例化 IConfiguration 实例 或者至少将我的文件包含到默认情况下构建的 IConfiguration asp net 中
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • 如何编写一个同时需要请求和响应Dtos的ServiceStack插件

    我需要提供本地化数据服务 所有本地化的响应 Dto 都共享相同的属性 IE 我定义了一个接口 ILocalizedDto 来标记那些 Dto 在请求端 有一个ILocalizedRequest对于需要本地化的请求 Using IPlugin
  • 等待线程完成

    private void button1 Click object sender EventArgs e for int i 0 i lt 15 i Thread nova new Thread Method nova Start list
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • Process.Start() 方法在什么情况下返回 false?

    From MSDN https msdn microsoft com en us library e8zac0ca v vs 110 aspx 返回值 true 表示有新的进程资源 开始了 如果由 FileName 成员指定的进程资源 St
  • 当另一个线程可能设置共享布尔标志(最多一次)时,是否可以读取共享布尔标志而不锁定它?

    我希望我的线程能够更优雅地关闭 因此我尝试实现一个简单的信号机制 我不认为我想要一个完全事件驱动的线程 所以我有一个工作人员有一种方法可以使用关键部分优雅地停止它Monitor 相当于C lock我相信 绘图线程 h class Drawi
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐

  • Nginx四层代理和七层代理的区别

    4层是指传输层的TCP UDP协议 7层是指应用层的HTTP协议 代理原理 4层代理 使用NAT Network Address Translation 技术 即网络地址转换 即请求进来的时候 nginx只修改数据包里面的目标IP 源IP
  • 浅谈密码破译

    关于密码破译 今天见到一篇文章 读来心中甚是激动 特记录在案 目录 密码破译 1 两种方式 2 双重验证系统2FA 3 密码登录的常见方式 3 1 基于 存储密码 的密码登录步骤 3 2 基于 密码散列 的密码登录步骤 4 暴力破解散列 密
  • 【NLP傻瓜式教程】手把手带你CNN文本分类(附代码)

    文章来源于NewBeeNLP 作者kaiyuan 写在前面 本文是对经典论文 Convolutional Neural Networks for Sentence Classification 1 的详细复现 应该是 基于TensorFlo
  • 200与mcgs485实例 smart_西门子Smart触摸屏与S7-200Smart无线PPI通讯实例

    在工业现场往往会用触摸屏来控制现场的PLC工作 若是遇到布线不方便或是工期较短的情况 那么可以采用无线数据交换的方式来完成触摸屏对PLC的RS485无线通讯 1 自由串口协议 2 Modbus协议 3 PPI协议 以下为大家介绍一种使用PP
  • 小结:token放在header中好处,HTTP Header详解(OAuth JWT等)

    1 Token机制相对于Cookie机制又有什么好处及基于JWT的Token认证机制实现 支持跨域访问 Cookie是不允许垮域访问的 这一点对Token机制是不存在的 前提是传输的用户认证信息通过HTTP头传输 引自 http www c
  • git status显示修改了大量文件

    diff git a Android mk b Android mk old mode 100644 new mode 100755 原来是filemode的变化 文件chmod后其文件某些位是改变了的 如果严格的比较原文件和chmod后的
  • Scratch编程入门-画图模块1【认识画图模块积木】

    在少儿编程软件Scratch中 拥有许多的拓展模块 在这些拓展模块里面 画笔模块 无疑是使用最多的模块之一 无论是中国电子学会的图形化编程考级题目还是线上线下的少儿编程比赛以及蓝桥杯甚至白名单的比赛题目中 使用该模块的画图类编程题目都是最重
  • C++ map用法总结

    1 map简介 map是STL的一个关联容器 它提供一对一的hash 第一个可以称为关键字 key 每个关键字只能在map中出现一次 第二个可以称为该关键字的值 value map以模板 泛型 方式实现 可以存储任意类型的数据 包括使用者自
  • 【数据结构】线性表的知识点全面总结

    目录 1 线性表的顺序表示 1 1顺序表的基本概念 1 2顺序表的基本操作 1 2 1插入 1 2 2删除 1 2 3查找 2 线性表的链式表示 2 1单链表 单链表的基本概念 2 1 1基本操作 2 1 1 1单链表的建立 2 1 1 2
  • android前端开发

    android前端开发 简单框架 1 Picasso 高性能图片下载库 在发现中使用 根据需求可以配合RevycleView使用 2 动画Animator 暂时不涉及 涉及到属性 差值器和估值器 3 事件分发机制 分发 拦截 响应 4 St
  • Keepalived + Haproxy实现负载均衡以及调度器的高可用

    Keepalived Haproxy Haproxy 提供高可用性 负载均衡以及基于 TCP 和 HTTP 应用的代理 支持虚拟主机 它是免费 快速并且可靠的一种解决方案 HAProxy 特别适用于那些负载特大的 web 站点 这些站点通常
  • torch.nn.init常用函数总结

    torch nn init中常用的函数 torch nn init uniform torch nn init normal torch nn init constant torch nn init ones torch nn init z
  • awk的BEGIN和END

    你可能对Unix比较熟悉 但你可能对Unix awk很陌生 这一点也不奇怪 的确 与其优秀的功能相比 awk还远没达到它应有的知名度 流程控制语句是任何程序设计语言都不能缺少的部分 任何好的语言都有一些执行流程控制的语句 Unix awk提
  • 详解高耦合低内聚,低耦合高内聚

    什么是高耦合低内聚 低耦合高内聚 耦合 不就是耦合系数高与低吗 就是关联性强不强 内聚 内聚是指是不是具有很强的功能性 一个模块或方法是不是只干一件事 越强的内聚或者高内聚模块应当恰好只做一件事 用面向对象举例 一个对象中有很多方法 每个方
  • 智能家居项目(基于HAL库开发附源码)

    文章目录 前言 需求分析 开发板 STM32F103 MIN 实物图 原理图 CubeMX环境搭建 GPIO设置 中断设置 代码分析 系统时钟配置 GPIO引脚初始化 USART1初始化 USART2初始化 NVIC初始化 功能代码编写 按
  • 使用配置文件(.settings、.config)存储应用程序配置

    引言 我不知大家早先是如何保存应用程序配置 以备下次打开时使用的 反正我开始学 Net的时候就去研究序列化 以二进制或XML格式的序列化来保存应用程序配置 这样每次都要建立单独的配置类 并书写读写配置代码 相当麻烦 期间也看了看 confi
  • 求某门课号的成绩高于某个同学(例如李勇)任意一门成绩的学生学号和成绩

    查询出在同一课程成绩高出李勇的学生学号和成绩 SELECT a 学号 a 课程号 a 成绩 FROM sc a sc b WHERE a 课程号 b 课程号 AND a 成绩 gt b 成绩 AND a 学号 b 学号 AND b 学号 s
  • 蓝桥杯——我该如何枚举

    文章目录 一 枚举 1 前言 2 枚举模板 二 例题分析 1 四平方和 1 题目描述 2 题目分析 3 代码实现 2 纯质数 1 题目描述 2 题目分析 3 代码实现 3 回文日期 1 题目描述 2 题目分析 3 代码实现 一 枚举 1 前
  • [从零开始学习FPGA编程-35]:进阶篇 - 基本时序电路-有限状态机简述(UML统一建模语言)

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 目录 前言 第1章 什么是有限状态机 1 1 什么是有限状态机
  • C++编程笔记(通信)(win32平台)

    目录 一 初始化网络库 二 socket套接字 2 1服务端 2 2客户端 三 发送 接收数据 3 1发送 3 2接收数据 四 自定义的结构体 4 1 发送端 4 2接收端 IPV6版本套接字的创建 一 初始化网络库 基本都得这么些 boo