【并发编程七】C++进程通信——套接字(socket)_80行代码实现一个聊天软件

news2024/9/28 19:23:22

【并发编程七】进程通信——套接字(socket)_80行代码实现一个聊天软件

  • 一、简介
  • 二、相关知识介绍
    • 1、winsock1.h、winsock2.h
    • 2、如何使用ws2_32.dll
    • 3、WSAStartup() 函数
    • 4、socket
    • 5、bind
    • 5、listen
    • 6、accept
    • 7、connect
  • 三、聊天软件的代码如下
    • 1、服务端
    • 2、客户端
  • 四、cmake构建、编译、运行
  • 五、输出
  • 六、c++网络通信的库

  • 前言:
    80行代码实现一个聊天软件。
    刚写完《【操作系统二】图解TCP/IP模型+实战》、和《【操作系统三】图解网络IO(bio\nio\slect\epoll)》,和网络通信相关的基本都介绍清楚了,所以原本这篇socket通信不想再写了,但是考虑到【并发编程】系列不完整,所以才写了下了这篇。所以本篇文章侧重Windows系统下socket的代码实战(写了一个简单的聊天软件),如果对什么是socket还没有清晰的印象,建议先读上面的两篇文章,然后再看本篇。

一、简介

套接字是什么?基于上面两篇文章,关于socket简单说两句。

  • 四元组
  • 近于应用层和传输控制层。
  • 通过系统调用,返回内核的文件描述符。
  • 阻塞和非阻塞在于,阻塞会在没有消息时会等待,非阻塞在没有消息时会返回一个错误,让程序继续向后运行。

二、相关知识介绍

1、winsock1.h、winsock2.h

WinSock(Windows Socket)编程依赖于系统提供的动态链接库(DLL),有两个版本:
较早的DLL是 wsock32.dll,对应的头文件为 winsock1.h;
最新的DLL是 ws2_32.dll,对应的头文件为 winsock2.h。

2、如何使用ws2_32.dll

使用 DLL 之前必须把 DLL 链接到当前程序,你可以在编译时链接,也可以在程序运行时链接,我们已在cmake系列《【cmake实战六】如何使用编译的库(动态库dll)——windows系统》、《【cmake实战七】如何使用编译的库(动态库dll)2——windows系统》进行了讲解。

  • 运行时链接
这里使用#pragma命令,在编译时加载:
#pragma comment (lib, "ws2_32.lib")
  • 编译时链接
target_link_libraries(Client "Ws2_32")

备注:本文使用的是编译时链接。

3、WSAStartup() 函数

使用 DLL 之前,还需要调用 WSAStartup() 函数进行初始化,以指明 WinSock 规范的版本,它的原型为:

  • parm1:请求的socket版本 2.2、2.1、2.0 ;parm2:传出的参数
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

4、socket

socket:创建套接字

  • parm1: af 地址协议族 ipv4 ipv6
  • parm2:type 传输协议类型 流式套接字(SOCK_STREAM),数据包套接字(SOCK_DGRAM)
  • parm3:ptotoc1 使用具体的某个传输协议
SOCKET WSAAPI socket(
  [in] int af,
  [in] int type,
  [in] int protocol
);

代码中我们使用的是ipv4,流式、TCP。

5、bind

  • 绑定ip端口号,绑定函数将本地地址与套接字相关联。
int WSAAPI bind(
  [in] SOCKET         s,
  [in] const sockaddr *name,
  [in] int            namelen
);

5、listen

  • 侦听函数将套接字置于侦听传入连接的状态。
int WSAAPI listen(
  [in] SOCKET s,
  [in] int    backlog
);

6、accept

  • accept 函数允许在套接字上尝试传入连接。
  • 等待客户都链接
SOCKET WSAAPI accept(
  [in]      SOCKET   s,
  [out]     sockaddr *addr,
  [in, out] int      *addrlen
);

7、connect

  • connect 函数建立与指定套接字的连接。
  • 客户端链接服务端。
int WSAAPI connect(
  [in] SOCKET         s,
  [in] const sockaddr *name,
  [in] int            namelen
);	

详细可以参考微软的官方文档winsock2.h 标头

三、聊天软件的代码如下

客户端和服务端分属于两个进程。(当然,本代码只是仅仅实现了socket客户端和服务端的聊天通信,并不设计到用户信息的注册、多客户端链接等。)

1、服务端

  • 过程
    • 初始化
    • 创建socket
    • 绑定端口号和IP
    • 监听端口
    • 接收服务端的链接
    • 接收数据
  • main.cpp
#include <winsock2.h>
#include<windows.h>
#include <iostream>
//#pragma comment (lib,"ws2_32.lib") 因为cmake里面我们使用了target_link_libraries(Server "Ws2_32"),所以,在次我们不需要使用静态链接了。否则你需要把这行放开
using namespace std;
int main()
{
	int errCode = 0;
	cout << "==============socket server begin start.=============="<<endl;
	
	{
		// step1:初始化套接字版本
		cout << "begin init socket." << endl;
		WSADATA wsadata;//wsa 即windows socket async 异步套接字
		errCode = WSAStartup(MAKEWORD(2, 2), &wsadata);   // parm1:请求的socket版本 2.2、2.1、2.0 ;parm2:传出的参数
		if(0 != errCode)
		{
			cout << "init socket version faile" << endl;
			return -1;
		}
		cout << "init socket version sucess" << endl;
	}
	SOCKET fd;
	{
		// setp2:创建套接字 ,我们使用的是ipv4,流式、TCP
		//parm1: af 地址协议族 ipv4 ipv6
		//parm2:type 传输协议类型 流式套接字(SOCK_STREAM),数据包套接字(SOCK_DGRAM)
		//parm3:ptotoc1 使用具体的某个传输协议
		fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (INVALID_SOCKET == fd)
		{
			cout << "create socket faile,get a invalid socket fd." << endl;
		}
		cout << "create socket sucess,get a valid socket fd." << endl;

		//setp2.1需要绑定的参数,主要是本地的socket的一些信息。
		SOCKADDR_IN addr;
		addr.sin_family = AF_INET;                           //地址协议族,和创建codket时必须一样。
		addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  //127.0.0.1
		//addr.sin_addr.S_un.S_addr = INADDR_ANY;            //INADDR_ANY:绑定到本地网卡的任意地址
		addr.sin_port = htons(8888);                         //端口 htons将无符号短整型转化为网络字节序

		//绑定ip端口号
		errCode = bind(fd, (SOCKADDR*)&addr, sizeof(SOCKADDR));
		if (SOCKET_ERROR == errCode)
		{
			cout << "bind ip port faile" << endl;
		}
		cout << "bind ip port sucess" << endl;

		//step2.3监听
		listen(fd, 5);
		cout << "create socket sucess!" << endl << "begin listen..." << endl << endl;
	}
	SOCKET fd_server;
	{
		//setp3,链接服务端
		fd_server = accept(fd, NULL, NULL);//于客户端建立链接
		if (INVALID_SOCKET == fd_server)
		{
			cout << "fd_server is invalid." << endl;
		}
		cout << "fd_server is valid." << endl<<endl;
	}
	
	cout << "==============begin talking.server ==============" << endl;
	while (1)
	{
		//step3.1,接收数据
		char receiveBuf[1024] = { 0 };
		errCode = recv(fd_server, receiveBuf, 1024, 0);
		if (errCode <= 0)
		{
			cout << "receive data faile" << endl;
		}
		cout << "receive>: "<<receiveBuf<<endl;

		char sendBuf[1024] = { 0 };
		cout << "send>: ";
		cin.getline(sendBuf, 1024);
		send(fd_server, sendBuf, 1024, 0);
	}
	//关闭服务端的socket
	closesocket(fd_server);//关闭
	closesocket(fd);//关闭
	WSACleanup();//释放资源
	return 0;
}

  • cmakelist
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

PROJECT(qq)

ADD_EXECUTABLE(Server main.cpp)

target_link_libraries(Server "Ws2_32")

ADD_SUBDIRECTORY(Client)

SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

2、客户端

  • 过程
    • 初始化
    • 创建socket
    • 链接服务端
    • 发送数据
  • main.cpp
#include <winsock2.h>
#include<windows.h>
#include <iostream>
//#pragma comment (lib,"ws2_32.lib") 因为cmake里面我们使用了target_link_libraries(Server "Ws2_32"),所以,在次我们不需要使用静态链接了。否则你需要把这行放开
using namespace std;
int main()
{
	int errCode = 0;
	cout << "==============socket client begin start.==============" << endl;
	
	{
		// step1:初始化套接字版本
		cout << "begin init socket." << endl;
		WSADATA wsadata;//wsa 即windows socket async 异步套接字
		errCode = WSAStartup(MAKEWORD(2, 2), &wsadata);   // parm1:请求的socket版本 2.2、2.1、2.0 ;parm2:传出的参数
		if (0 != errCode)
		{
			cout << "init socket version faile" << endl;
			return -1;
		}
		cout << "init socket version sucess" << endl;
	}
	SOCKET fd;
	{
		// setp2:创建套接字 ,我们使用的是ipv4,流式、TCP
		//parm1: af 地址协议族 ipv4 ipv6
		//parm2:type 传输协议类型 流式套接字(SOCK_STREAM),数据包套接字(SOCK_DGRAM)
		//parm3:ptotoc1 使用具体的某个传输协议
		fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (INVALID_SOCKET == fd)
		{
			cout << "create socket faile,get a invalid socket fd." << endl;
		}
		cout << "create socket sucess,get a valid socket fd." << endl;

		//setp2.1需要绑定的参数,主要是本地的socket的一些信息。
		SOCKADDR_IN addr;
		addr.sin_family = AF_INET;                           //地址协议族,和创建codket时必须一样。
		addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  //127.0.0.1
		//addr.sin_addr.S_un.S_addr = INADDR_ANY;            //INADDR_ANY:绑定到本地网卡的任意地址
		addr.sin_port = htons(8888);                         //端口 htons将无符号短整型转化为网络字节序

		//setp3,接收客户端的链接
		errCode = connect(fd, (SOCKADDR*)&addr, sizeof(SOCKADDR));
		if (SOCKET_ERROR == errCode)
		{
			cout << "connect faile" << endl;
			return -1;
		}
		cout << "connect sucess" << endl<<endl;
	}

	cout << "==============begin talking.client==============" << endl;
	while (1)
	{
		//step3.1,发送数据
		cout << "send>: ";
		char sendBuf[1024] = {0};
		cin.getline(sendBuf,1024);
		if (SOCKET_ERROR == send(fd, sendBuf, 1024, 0))
		{
			cout << "send data error" << endl;
			return -1;
		}

		// step3.2,接收数据
		char receiveBuf[1024];
		errCode = recv(fd, receiveBuf, 1024, 0);
		if (errCode <= 0)
		{
			cout << "receive data faile" << endl;
		}
		cout << "receive>" << receiveBuf << endl;
	}

	//关闭客户端的socket
	closesocket(fd);//关闭
	WSACleanup();//释放资源
	return 0;
}

  • cmakelist
CMAKE_MINIMUM_REQUIRED(VERSION 3.8.0)

SET(TARGET "Client")

ADD_EXECUTABLE(Client main.cpp)

target_link_libraries(Client "Ws2_32")

SET(LIBRARY_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")
SET(EXECUTABLE_OUTPUT_PATH "${PROJECT_SOURCE_DIR}/lib")

四、cmake构建、编译、运行

文件目录如下
在这里插入图片描述

1、构建

cmake -B build

2、编译
当然,你也可以使用vs手动编译

cmake --build build

3、生成的项目组下图
在这里插入图片描述

五、输出

  • 客户端
    在这里插入图片描述

  • 服务端
    在这里插入图片描述

  • 客户端、服务端
    在这里插入图片描述

六、c++网络通信的库

  • 1、c++用途这么广泛的语言,竟然没有一个标准的c++库。
  • 2、之前在某位大佬的文章看到,说是c++23或者c++26,可能会把网络通信引入c++标准库。
  • 3、除了本文说的调用系统函数,还可以使用第三方库来实现网络通信。
  • 4、第三方网络库对各个系统的兼容性、和性能未知,所以如果要做跨平台开发的化,可以再多做些调研。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/96892.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT系列第5节 QT中常用输入控件

QT中经常利用控件来获取用户输入数据&#xff0c;本篇将介绍常用的用户输入控件 目录 (1) QSpinBox (2) QDoubleSpinBox &#xff08;3&#xff09;QSlider &#xff08;4&#xff09; QScrollBar &#xff08;5&#xff09;QProgressBar &#xff08;6&#xff09;QDia…

@MapperScan原理探究

1. 前言 MyBatis在整合Spring的时候&#xff0c;只需要加如下注解&#xff0c;就可以将Mapper实例注册到IOC容器交给Spring管理&#xff0c;它是怎么做到的呢&#xff1f;&#xff1f;&#xff1f; MapperScan("com.xxx.mapper")提出几个问题&#xff1a; Mapper接…

Snipaste的使用

Snipaste截屏软件的使用&#xff1a; 1、开始截屏 第一种方式&#xff1a;快捷键 &#xff08;默认是F1&#xff09;也就是说按一下F1键就会进入截屏状态。 第二种方式&#xff1a;点击软件在任务栏上的图标。 2、选定截屏区域&#xff1a; 进入截屏状态后移动鼠标&#x…

绩效考核管理方案

第一部分 总 则 第一条&#xff1a;目的 1、通过绩效考核&#xff0c;传递组织目标和压力&#xff0c;促使员工提高工作绩效&#xff0c;达到“培养员工、提高员工的工作能力、纠正员工偏差、使之更好地为公司服务&#xff0c;达到公司与个人之间的双赢”的目的。 2、加强公司的…

四、fs文件系统模块

fs模块是Node.js官方提供用来操作文件的模块&#xff0c;属于核心模块&#xff0c;提供了一些列的方法和属性&#xff0c;用来满足用户的操作需求&#xff1b; 引入fs模块 const fs require(fs); fs.readFile() 读取 读取指定的内容&#xff0c;fs.readFile(path[&#xff…

图文排版 之 line-height

图文排版 之 line-height 设置行盒子的高度. line-height 经常被用来设置多行文本的行间距. 对于块级元素, line-height 制定了行盒子的最小高度. 对于非替换的内联元素, line-height 的值用来计算行盒子的高度. 一般来说, 大家都知道一个 div 的高度默认是由其子元素撑起的, …

史上最全 Java 高频面试合集,命中率高达 95%

进大厂是大部分程序员的梦想&#xff0c;而进大厂的门槛也是比较高的&#xff0c;所以这里整理了一份阿里、美团、滴滴、头条等大厂面试大全&#xff0c;其中概括的知识点有&#xff1a;Java、MyBatis、ZooKeeper、Dubbo、Elasticsearch、Memcached、 Redis、MySQL、Spring、S…

现代控制理论

系统的状态空间表达式的建立 建立系统状态空间表达式的三种方法 &#xff08;1&#xff09;根据系统的方框图列写 &#xff08;2&#xff09;从系统的基本原理推导 &#xff08;3&#xff09;根据传递函数或者高阶微分方程实现 方框图法 有些系统的系统机理还没搞清楚可以使…

nodejs+vue高校教室管理系统

摘 要 1 1 系统概述 4 1.1研究背景 4 1.2研究现状 4 1.3主要内容 5 2 系统开发环境 6 2.3 MySql数据库 6 2.4 B/S结构 7 3 需求分析 8 3.1技术可行性&#xff1a;技术背景 8 3.2经济可行性 8 3.3操作可行性 8 3.4系统设计规则 9 3.5系统流程和逻辑 9 4系统概要设计 13 4.1 概…

Delaunay三角网之分治算法

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 Delaunay三角网现有的构网算法有很多种,有学者曾对其中一些算法进行了调查和评估(如下图所示)。结果表明,在少量点时,Lawson的增量插入算法、Lee和Schachter的分治算法以及Fortune的平面扫描算法在速度上大致相…

Hexo+stun主题+Gitee5分钟快速搭建你的个人Blog

环境配置 首先要下载Node.js&#xff0c;然后安装Git&#xff0c;接着注册码云&#xff0c;最后安装Hexo&#xff0c;Hexo安装命令行(cmd 或 gitbash): npm install hexo-cli -g速度慢可以先改一下安装源: npm config set registry https://registry.npm.taobao.orgHexo搭建 目…

Linux调试器-gdb介绍

文章目录gdb的基础使用gdb是什么gdb的使用gdb的下载**l 显示代码****b 行号 :打断点****info b :查看断点****d 断点序号 :删除断点****r :运行调试****n&#xff08;next&#xff09; &#xff1a;逐过程****s&#xff08;step&#xff09;&#xff1a;逐语句****c&#xff08…

程序员接私活的那些事

每日坚持一点点&#xff0c;就离目标近一点。 文章目录怎么在空闲时间获得格外的收入接私活的有哪些途径接私活的途中需要注意什么格外福利怎么在空闲时间获得格外的收入 今天我们就不聊技术问题&#xff0c;咱们聊聊一个有意思的话题—>怎么在空闲的时间获得格外的收益&…

设计模式3 - 结构型模式

23种设计模式分析与见解开篇、UML、软件设计原则https://blog.csdn.net/lili40342/article/details/128358435创建型模式https://blog.csdn.net/lili40342/article/details/128358392结构型模式https://blog.csdn.net/lili40342/article/details/128358313行为型模式https://bl…

UNIAPP实战项目笔记54 登录时用state存储用户信息并持久化用户登录和退出登录功能

UNIAPP实战项目笔记54 登录时用state存储用户信息并持久化用户登录和退出登录功能 登录信息各个页面同步使用的是state 登录信息的持久化使用的是本地存储 打开APP自动初始化本地存储数据到state中 实际案例图片 登录页面数据渲染 代码 login.vue页面 登录成功后显示的页面 &l…

论文阅读 - Social Bot-Aware Graph Neural Network for Early Rumor Detection - CCF B

目录 摘要&#xff1a; 1 绪论 2 问题定义 3 SBAG模型 3.1社交机器人检测 3.2 机器人感知图神经网络 3.2.1基于GCN的用户发布 3.2.2 基于GAT的用户交互 3.2.3文本编码器 3.2.4 输出层 3.3 训练 4 实验 4.1 数据集 4.2 实验设置 4.3 基线 4.4 实验结果 4.4.1 谣言…

对时间序列数据(牛仔裤销售数据集)进行LSTM预测(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 LSTM模型的一个常见用途是对长时间序列数据进行学习预测&#xff0c;例如得到了某商品前一年的日销量数据&#xff0c;我们可以…

【GPU】Nvidia CUDA 编程高级教程——利用蒙特卡罗法求解近似值(NVSHMEM)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

MyBatis + SQL Server Using Table-Valued Parameters

一、实现原理 参考文档 Using table-valued parametersSystem requirements for the JDBC driverMicrosoft JDBC Driver for SQL Server1、微软官方封装了 JDBC 驱动 jar 包&#xff0c;提供 SQLServerDataTable 类&#xff1b; 2、Mybatis 官方提供自定义类型处理接口 TypeHa…

Python学习笔记-Pygame

目录 一、Pygame概述 1.安装Pyganme 2.Pygame常用模块介绍 2.1 display模块常用方法 2.2 pygame.event模块常用方法 2.3 Surface对象的常用方法 记述关于Pyganme开发的基本知识。 一、Pygame概述 Pygame是跨平台的python模块&#xff0c;转为电子游戏设计&#xff08;包…