C++编程笔记(通信)(win32平台)
一、初始化网络库
基本都得这么些
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平台)的更多相关文章
- C++windows内核编程笔记day11 win32静态库和动态库的使用
windows库程序: 静态库: 源码被链接到调用的程序或动态库,被调用时,代码最少有1份,文件后缀.LIB 动态库: 函数被程序或其它动态库调用,被调用时,代码仅仅有1份,文件后缀.DLL 静态库( ...
- cocos2d-x实战 C++卷 学习笔记--第4章 win32平台下中文乱码问题
前言: 将GBK编码的字符串转为UTF-8编码.(通俗点说就是解决中文乱码问题) 简要介绍: 在Win32平台下通过 log 输出中文字符时,会出现中文乱码问题.同样的代码在 ios 和 Androi ...
- python核心编程--笔记
python核心编程--笔记 的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找pyt ...
- storysnail的Windows串口编程笔记
storysnail的Windows串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据 ...
- python核心编程--笔记(不定时跟新)(转)
的解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v ...
- Windows网络编程笔记4 -- Winsock 协议相关知识
Win32平台上的Winsock编程,Winsock是一个与协议无关的接口.以下协议是我们需要了解的: 网络协议的特征包括: 1. 面向消息 2. 面向连接和无线接 3. 可靠性和次序性 4. ...
- Linux网络编程笔记(修订版)
我的网络编程笔记, 因为最近又要做Linux下的网络编程,故重新修订, 其中一些内容参考了文末的链接及文章 1. 基本概念 2. 基本接口 2.1. 打开一个socket 2.2. 将 ...
- python核心编程笔记(转)
解释器options: 1.1 –d 提供调试输出 1.2 –O 生成优化的字节码(生成.pyo文件) 1.3 –S 不导入site模块以在启动时查找python路径 1.4 –v 冗 ...
- storysnail的Linux串口编程笔记
storysnail的Linux串口编程笔记 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据Ge ...
- 笔记整理--Linux平台MYSQL的C语言
Linux平台MYSQL的C语言API全列表 - 第三只眼的专栏 - 博客频道 - CSDN.NET - Google Chrome (2013/8/18 22:28:58) Linux平台MYS ...
随机推荐
- Lock 锁底层实现
★ 1.讲讲 Lock 锁 是一个接口,有三个实现类,分别是常用的 可重入锁,读锁.写锁.常用的是可重入锁. 加锁使用lock() 方法,解锁使用 unlock() 方法.Lock的底层是 AQS+C ...
- 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用(含源码)
前言 今天我们一起来使用LabVIEW AI视觉工具包快速实现图像的滤波与增强:图像灰度处理:阈值处理与设定:二值化处理:边缘提取与特征提取等基本操作.工具包的安装与下载方法可见之前的博客. 一.图像 ...
- aws-s3-国际global与国内CN的一些说明
S3云存储国际版最近经常被墙,国内部分地区有时能正常上传下载,有时也会直接报错网络错误等信息, 所以建议S3使用国内AWS的.国内S3与国外S3在使用时需要注意以下几点: 1)URL不通用 国际版的S ...
- 洛谷P1884 [USACO12FEB]Overplanting S (矩形切割)
一种矩形切割的做法: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const in ...
- 驱动开发:内核遍历进程VAD结构体
在上一篇文章<驱动开发:内核中实现Dump进程转储>中我们实现了ARK工具的转存功能,本篇文章继续以内存为出发点介绍VAD结构,该结构的全程是Virtual Address Descrip ...
- TomCat之安装
TomCat 之安装(伪分布式版本) 本次安装是使用的伪分布式的安装(即一台机器安装两个tomcat) 1.通过scp导入tomcat安装包 2.解压缩成俩个文件 3.修改第一个tomcat的配置文件 ...
- uoj221【NOI2016】循环之美
前面部分比较简单,就是无脑化式子,简单点讲好了. 首先肯定在\((x,y)=1\)时才考虑这个分数,要求纯循环的话,不妨猜猜结论,就是y必须和K互质.所以答案是\(\sum_{i=1}^n \sum_ ...
- 成功解决:snippet设置的开机自启没有效果
1.问题描述 勾选开机启动后.没有效果.每次开机都要我重新找到对应的安装目录.双击运行开启 2.解决方法 将snipaste的快捷方式放到开机启动目录下 C:\Users\Administrator\ ...
- json文本数据
本文主要针对三个问题:json格式数据,text数据与json数据之间的关系,json和python字典的区别 1.什么是json数据? json是文本数据,可以在网络中传输的通用数据,它是具有特定格 ...
- C# 8.0 添加和增强的功能【基础篇】
.NET Core 3.x和.NET Standard 2.1支持C# 8.0. 一.Readonly 成员 可将 readonly 修饰符应用于结构的成员,来限制成员为不可修改状态.这比在C# 7. ...