一、初始化网络库

基本都得这么些

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

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

  1. C++windows内核编程笔记day11 win32静态库和动态库的使用

    windows库程序: 静态库: 源码被链接到调用的程序或动态库,被调用时,代码最少有1份,文件后缀.LIB 动态库: 函数被程序或其它动态库调用,被调用时,代码仅仅有1份,文件后缀.DLL 静态库( ...

  2. cocos2d-x实战 C++卷 学习笔记--第4章 win32平台下中文乱码问题

    前言: 将GBK编码的字符串转为UTF-8编码.(通俗点说就是解决中文乱码问题) 简要介绍: 在Win32平台下通过 log 输出中文字符时,会出现中文乱码问题.同样的代码在 ios 和 Androi ...

  3. python核心编程--笔记

    python核心编程--笔记 的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找pyt ...

  4. storysnail的Windows串口编程笔记

    storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...

  5. python核心编程--笔记(不定时跟新)(转)

    的解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   ...

  6. Windows网络编程笔记4 -- Winsock 协议相关知识

     Win32平台上的Winsock编程,Winsock是一个与协议无关的接口.以下协议是我们需要了解的: 网络协议的特征包括: 1.  面向消息 2.  面向连接和无线接 3.  可靠性和次序性 4. ...

  7. Linux网络编程笔记(修订版)

    我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1.   基本概念 2.   基本接口 2.1.   打开一个socket 2.2.   将 ...

  8. python核心编程笔记(转)

    解释器options: 1.1 –d   提供调试输出 1.2 –O   生成优化的字节码(生成.pyo文件) 1.3 –S   不导入site模块以在启动时查找python路径 1.4 –v   冗 ...

  9. storysnail的Linux串口编程笔记

    storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...

  10. 笔记整理--Linux平台MYSQL的C语言

    Linux平台MYSQL的C语言API全列表 - 第三只眼的专栏 - 博客频道 - CSDN.NET - Google Chrome (2013/8/18 22:28:58)   Linux平台MYS ...

随机推荐

  1. Elasticsearch:fielddata 介绍

    默认情况下,大多数字段都已编入索引,这使它们可搜索. 但是,脚本中的排序,聚合和访问字段值需要与搜索不同的访问模式. 搜索需要回答"哪个文档包含该术语?"这个问题,而排序和汇总则需 ...

  2. Elastic:使用ElastAlert发送通知

    ElastAlert是一个简单的框架,用于从Elasticsearch中的数据中发出异常,尖峰或其他感兴趣模式的警报.我们可以在地址https://elastalert.readthedocs.io/ ...

  3. 天翼云主机某一IP多次登录失败导致IP被锁无法登录,天翼云主机莫名其妙无法远程登陆

    情况说明: 直接使用该IP通过ssh远程连接失败,但是先通过ssh远程连接其他主机上,然后在这个主机上再ssh刚才连接失败的主机,就能登陆上. 说明,root用户不是被锁了, 而是远程登陆IP被锁了 ...

  4. 生产环境中使用Kibana

    在 Kibana 中使用 X-Pack 使用 X-Pack 安全模块 控制用户通过 Kibana 可以访问哪些 Elasticsearch 数据. 当安装 X-Pack 时,Kibana 用户必须登陆 ...

  5. ELK 性能优化实践

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI5MTU1MzM3MQ==&mid=2247489814&idx=1&sn=6916f8b7 ...

  6. Ubuntu20.04本地安装Redash中文版

    一.安装基础环境: # 1.更换APT国内源 sudo sed -i s@/cn.archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources ...

  7. esp-idf 安装(Windows )

    esp32的开发有两种环境,分别是 Arduino 和 esp32-idf. Arduino 是在 esp32-idf 基础上进行封装的,虽然使用起来比较方便,但是能自由更改的就变少了,适合新手使用. ...

  8. localStorage概要

    在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localSt ...

  9. 矩阵顺时针打印(C++)(? 为什么不能AC,9度1391)

    #include <iostream> #include <fstream> using namespace std; int a[1000][1000]; void prin ...

  10. vulnhub靶场之RED: 1

    准备: 攻击机:虚拟机kali.本机win10. 靶机:RED: 1,地址我这里设置的桥接,,下载地址:https://download.vulnhub.com/red/Red.ova,下载后直接Vi ...