【并发编程七】进程通信——套接字(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);}//关闭服务端的socketclosesocket(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;}//关闭客户端的socketclosesocket(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、第三方网络库对各个系统的兼容性、和性能未知,所以如果要做跨平台开发的化,可以再多做些调研。

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

  1. 网络编程(网络基础、套接字Socket、数据报Datagram及其常用方法)

    章节内容 套接字Socket 数据报Datagram 章节目标 了解网络通信中的IP,端口和协议 掌握套接字的使用 熟悉数据报的使用 一.网络基础 1.软件结构 C/S结构 C => Clien ...

  2. python并发编程之semaphore(信号量)_python 之 并发编程(守护进程、互斥锁、IPC通信机制)...

    9.5 守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就立即终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic process ...

  3. [python学习] 专题七.网络编程之套接字Socket、TCP和UDP通信实例

    很早以前研究过C#和C++的网络通信,参考我的文章:                  C#网络编程之Tcp实现客户端和服务器聊天                 C#网络编程之套接字编程基础知识   ...

  4. Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象

    一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字server.bind() #把地址绑定到套接字,网络地址加端口server.liste ...

  5. Day4:应用层——网络应用层内容概述、网络应用基本原理(体系结构、进程通信、套接字socket、应用层协议)、Web应用与HTTP

    加油!寒假偷博人 一.网络应用层内容概述. 1本篇内容:(原理.实例.编程) 网络应用体系结构 客户机/服务器  P2P  混合结构 网络应用的服务需求 可靠性 带宽 时延 Inte ...

  6. Python3 与 C# 并发编程之~进程先导篇

    在线预览:http://github.lesschina.com/python/base/concurrency/1.并发编程-进程先导篇.html Python3 与 C# 并发编程之- 进程篇:h ...

  7. python socket编程之双方相互通信简单实例_Python socket实现的简单通信功能示例

    套接字(socket)是计算机网络数据结构,在任何类型的通信开始之前,网络应用程序必须创建套接字,可以将其比作电话的插孔,没有它将无法进行通信 常用的地址家族 AF_UNIX:基于文件,实现同一主机不 ...

  8. 套接字编程---2(TCP套接字编程的流程,TCP套接字编程中的接口函数,TCP套接字的实现,TCP套接字出现的问题,TCP套接字多进程版本,TCP套接字多线程版本)

    TCP模型创建流程图 TCP套接字编程中的接口 socket 函数 #include <sys/types.h> /* See NOTES */ #include <sys/sock ...

  9. tcp/ip网络编程--accept()函数返回的套接字

    tcp/ip网络编程–accept()函数返回的套接字 套接字:1)套接字是对网络中不同主机的应用进程之间进行双向通信的端点的抽象:一个套接字就是网络进程通信的一端.[1] 2)套接字是用来与另一个进 ...

最新文章

  1. Android Training - Volley(Lesson 0 - 序言)
  2. Hadoop框架:集群模式下分布式环境搭建
  3. Pytorch 配置 Tensorboard 可视化学习(一)
  4. python中functools_(转)Python——functools
  5. R pdf大小_PDF如何压缩?这两招教您快速压缩PDF!
  6. 基于STM32cubemx的STM32F107vct6的代码生成,实验四串口与DMA
  7. [育强飞]自动刷屏器_育强飞_新浪博客
  8. 家用计算机常见故障及解决方式,计算机常见故障及解决方法
  9. HTML中 src alt align 分别是什么意思?
  10. WES分析7-VCF
  11. 在github上托管属于自己的网页
  12. DAX: 复购率计算
  13. 改变文字颜色html,html怎么改变字体大小和颜色
  14. 【Vue项目复习笔记】详情页的展示
  15. c语言驱动显卡,佳能 Tesla C2050 驱动程序下载-更新佳能软件(显卡)
  16. 【slowfast中ava数据集处理】ava数据集,将原视频裁剪为15分钟每段
  17. 堆栈溢出:Stack overflow (参数: 0x0000000000000001, 0x0000005410A03FF8)
  18. maya批量操作mel_maya为热键指定 MEL 脚本,MAYA
  19. 计算机系统有硬件系统和软件系统两大部分组成
  20. 太空避难所修改数据(船员和货币)

热门文章

  1. 从DRA音频标准(国标级)来看技术创新(二)
  2. 【排序算法】选择排序(C语言)
  3. web.config 中connectionStrings连接字符串加密、解密问题
  4. onmousemove、onmouseout事件
  5. 知识管理系统中的在线编辑,让共享协作更简便
  6. 基于annotation方式的通用报文处理框架
  7. jmockit 覆盖率配置
  8. JavaFX遇到错误JavaFX Invalid URL: Invalid URL or resource not found的一种原因
  9. Linux毕业设计:基于OpenCV和QT库实现的人脸识别考勤/门禁系统(arm嵌入式ubuntu)
  10. JDX-1100 DC220V\\DC220V静态信号继电器