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 ...
随机推荐
- 【疑难杂症】奇异值分解(SVD)原理与在降维中的应用
前言 在项目实战的特征工程中遇到了采用SVD进行降维,具体SVD是什么,怎么用,原理是什么都没有细说,因此特开一篇,记录下SVD的学习笔记 参考:刘建平老师博客 https://www.cnblogs ...
- 使用读写分离模式扩展 Grafana Loki
转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247500127&idx=1&sn=995987d558 ...
- Python 类型提示简介
Python 3.6+ 版本加入了对"类型提示"的支持. 这些"类型提示"是一种新的语法(在 Python 3.6 版本加入)用来声明一个变量的类型. 通过声明 ...
- 第2篇----Istio架构概述篇
Istio的工作机制 Istio的工作机制和架构,分为控制面和数据面两部分.控制面主要包括Pilot.Mixer.Citadel等服务组件:数据面由伴随每个应用程序部署的代理程序Envoy组成,执行针 ...
- C++ 自学笔记 访问限制 Setting limits
Setting limits 让客户不能改,让设计者可以改 C++: 任何人访问 成员函数访问(同一个类的不同实例化对象可以相互访问私有成员变量) 类自己或子类访问 friend: 朋友就可以授权访问 ...
- 洛谷P1962 斐波那契数列 (矩阵快速幂)
学了矩阵,练一下手... 1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const ll mod=1e9+7; 4 using n ...
- 《吐血整理》高级系列教程-吃透Fiddler抓包教程(28)-Fiddler如何抓取Android7.0以上的Https包-下篇
1.简介 虽然依旧能抓到大部分Android APP的HTTP/HTTPS包,但是别高兴的太早,有的APP为了防抓包,还做了很多操作:① 二次加密有的APP,在涉及到关键数据通信时,会将正文二次加密后 ...
- 9.为url添加可选的后缀
为url添加可选的后缀 在drf的机制中,响应数据的格式不再与单一内容类型连接,可以同时享有json格式或html格式,我们可以为api路径添加格式后缀的支持,使用格式后缀给我们明确指定了给定格式的u ...
- 现在入行Java真的还有出路吗?
现在入行Java还来的及吗?未来该如何抉择? 最近有点迷茫了,互联网好像一夜之间进入了寒冬一样,到处都是就业难的样子,当然,不可否认的是,那些努力的大佬依旧是斩获多个offer,但是,窝就一个小镇做题 ...
- 在FreeSQL中实现「触发器」和软删除功能
前言 最近做新项目,技术栈 AspNetCore + FreeSQL 这个ORM真的好用,文档也很完善,这里记录一下两个有关「触发器」的功能实现 修改实体时记录更新时间 模型代码 我的模型都是基于这个 ...