【并发编程七】C++进程通信——套接字(socket)
家电修理 2023-07-16 19:16www.caominkang.com电器维修
【并发编程七】进程通信——套接字(socket)_80行代码实现一个聊天软件
- 一、简介
- 二、相关知识介绍
- 1、insock1.h、insock2.h
- 2、如何使用s2_32.dll
- 3、WSAStartup() 函数
- 4、socket
- 5、bind
- 5、listen
- 6、aept
- 7、connect
- 三、聊天软件的代码如下
- 1、服务端
- 2、客户端
- 四、cmake构建、编译、运行
- 五、输出
- 六、c++网络通信的库
- 前言
80行代码实现一个聊天软件。
刚写完《【操作系统二】图解TCP/IP模型+实战》、和《【操作系统三】图解网络IO(bionioslectepoll)》,和网络通信相关的基本都介绍清楚了,所以原本这篇socket通信不想再写了,考虑到【并发编程】系列不完整,所以才写了下了这篇。所以本篇文章侧重Windos系统下socket的代码实战(写了一个简单的聊天软件),如果对什么是socket还没有清晰的印象,建议先读上面的两篇文章,然后再看本篇。
套接字是什么?基于上面两篇文章,关于socket简单说两句。
- 四元组
- 近于应用层和传输控制层。
- 通过系统调用,返回内核的文件描述符。
- 阻塞和非阻塞在于,阻塞会在没有消息时会等待,非阻塞在没有消息时会返回一个错误,让程序继续向后运行。
WinSock(Windos Socket)编程依赖于系统提供的动态链接库(DLL),有两个版本
- 较早的DLL是 sock32.dll,对应的头文件为 insock1.h;
- 最新的DLL是 s2_32.dll,对应的头文件为 insock2.h。
使用 DLL 之前必须把 DLL 链接到当前程序,你可以在编译时链接,也可以在程序运行时链接,我们已在cmake系列《【cmake实战六】如何使用编译的库(动态库dll)——indos系统》、《【cmake实战七】如何使用编译的库(动态库dll)2——indos系统》进行了讲解。
- 运行时链接
这里使用#pragma命令,在编译时加载 #pragma ment (lib, "s2_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 VersionRequested, 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、aept
- aept 函数允许在套接字上尝试传入连接。
- 等待客户都链接
SOCKET WSAAPI aept( [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 );
详细可以参考微软的官方文档insock2.h 标头
三、聊天软件的代码如下客户端和服务端分属于两个进程。(,本代码只是仅仅实现了socket客户端和服务端的聊天通信,并不设计到用户信息的注册、多客户端链接等。)
1、服务端- 过程
- 初始化
- 创建socket
- 绑定端口号和IP
- 监听端口
- 接收服务端的链接
- 接收数据
- main.cpp
#include#include #include //#pragma ment (lib,"s2_32.lib") 因为cmake里面我们使用了target_link_libraries(Server "Ws2_32"),所以,在次我们不需要使用静态链接了。否则你需要把这行放开 using namespace std; int main() { int errCode = 0; cout << "==============socket server begin start.=============="< // step1:初始化套接字版本 cout << "begin init socket." << endl; WSADATA sadata;//sa 即indos socket async 异步套接字 errCode = WSAStartup(MAKEWORd(2, 2), &sadata); // 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 = i_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 = aept(fd, NULL, NULL);//于客户端建立链接 if (INVALID_SOCKET == fd_server) { cout << "fd_server is invalid." << endl; } cout << "fd_server is valid." << endl< //step3.1,接收数据 char receiveBuf[1024] = { 0 }; errCode = recv(fd_server, receiveBuf, 1024, 0); if (errCode <= 0) { cout << "receive data faile" << endl; } cout << "receive>: "< 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#include #include //#pragma ment (lib,"s2_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 sadata;//sa 即indos socket async 异步套接字 errCode = WSAStartup(MAKEWORd(2, 2), &sadata); // 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 = i_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< //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、生成的项目组下图
-
客户端
-
服务端
-
客户端、服务端
- 1、c++用途这么广泛的语言,竟然没有一个标准的c++库。
- 2、之前在某位大佬的文章看到,说是c++23或者c++26,可能会把网络通信引入c++标准库。
- 3、除了本文说的调用系统函数,还可以使用第三方库来实现网络通信。
- 4、第三方网络库对各个系统的兼容性、和性能未知,所以如果要做跨平台开发的化,可以再多做些调研。
空调维修
- 温岭冰箱全国统一服务热线-全国统一人工【7X2
- 荆州速热热水器维修(荆州热水器维修)
- 昆山热水器故障码5ER-昆山热水器故障码26
- 温岭洗衣机24小时服务电话—(7X24小时)登记报
- 统帅热水器售后维修服务电话—— (7X24小时)登
- 阳江中央空调统一电话热线-阳江空调官方售后电
- 乌鲁木齐阳春燃气灶厂家服务热线
- 珠海许昌集成灶售后服务电话-全国统一人工【
- 乌鲁木齐中央空调维修服务专线-乌鲁木齐中央空
- 新沂热水器故障电话码维修-新沂热水器常见故障
- 诸城壁挂炉24小时服务热线电话
- 靖江空调24小时服务电话-——售后维修中心电话
- 空调室外滴水管维修(空调室外排水管维修)
- 九江壁挂炉400全国服务电话-(7X24小时)登记报修
- 热水器故障码f.22怎么解决-热水器f0故障解决方法
- 营口热水器售后维修服务电话—— 全国统一人工