c++: websocket 客户端与服务端之间的连接交互

这篇具有很好参考价值的文章主要介绍了c++: websocket 客户端与服务端之间的连接交互。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

socket

头文件

延迟时间

通信协议地址

TCP/IP

服务端

客户端

编程步骤

服务端

客户端

编程步骤

1. 初始化 WSAStartup

2. 创建 socket

2.1 协议族

2.2 socket 类型

2.3 协议

3. 绑定 bind (服务端)

4. 监听 listen(服务端)

5. 请求连接 connect(客户端)

6. 接收请求 accept(服务端)

7. 发送数据 send(客户端)

8. 接收数据 recv(服务端)

9. 关闭 closesocket

10. 停止使用 WSACleanup

实例

服务端

客户端

异常


socket

    用于描述地址和端口,是一个通信链句柄。

    套接字编程有三种:

  1. 流式套接字(SOCK_STREAM):面向连接,准确无误,但效率慢,要求数据正确性。

  2. 数据报套接字(SOCK_DGRAM):无连接,只传输,无数据校验、效率高。

头文件

#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")

延迟时间

struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* microseconds */
};
  • tv_sec : 毫秒;

  • tx_usec : 微妙。

通信协议地址

#include <netinet/in.h>

// ipv4
struct sockaddr_in
{
    sa_family_t sin_family; // 地址族(Address Family)
    uint16_t sin_port; // 16位TCP/UDP端口号
    struct in_addr sin_addr; // 32位 IP地址
    char sin_zero[8]; // 不使用
}

struct in_addr
{
    In_addr_t s_addr; // 32位 IPv4地址
}
  • sin_port、sin_addr : 必须是网络字节序(NBO);

  • 一般可视化数字 : 主机字节序(HBO);

  • 该结构体解决了 sockaddr(sin_family、sa_data[14]) 的缺陷:把目标地址和端口信息混合在一起;

  • sockaddr_in 是 internet 环境下套接字的地址形式;

补充如下:

// ipv6
struct sockaddr_in6 { 
    sa_family_t     sin6_family;   /* AF_INET6 */ 
    in_port_t       sin6_port;     /* port number */ 
    uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
    struct in6_addr sin6_addr;     /* IPv6 address */ 
    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
};
struct in6_addr { 
    unsigned char   s6_addr[16];   /* IPv6 address */

TCP/IP

    客户端向服务器发出请求,服务器接收请求后,提供相应的服务。

服务端

    建立 socket,声明自身的端口号和地址并绑定到 socket,使用 listen 打开监听,然后不断用 accept 去查看是否有连接,有就捕获 socket,并通过 recv 获取消息的内容,通信完成后调用 closeSocket 关闭这个对应 accept 到的 socket ,如果不再需要等待则 closeSocket 关闭自身 socket。

客户端

    建立 socket,通过端口号和地址确定目标服务器,使用 Connect 连接到服务器,send 发送消息,等待处理,通信完成后调用 closeSocket 关闭 socket。

编程步骤

服务端

1、加载套接字库,创建套接字(WSAStartup() / socket() );

2、绑定套接字到一个 IP 地址和一个端口上( bind() );

3、将套接字设置为监听模式等待连接请求( listen() );

4、请求到后,接收连接请求,返回一个新的对应于此连接的套接字( accept() );

5、用返回的套接字和客户端进行通信(send() / recv());

6、返回,等待另一个连接请求;

7、关闭套接字,关闭加载的库(closeSocket() / WSACleanup())。

客户端

1、加载套接字库,创建套接字(WSAStartup() / socket());

2、向服务器发出连接请求(connect());

3、和服务器进行通信(send() / recv());

4、关闭套接字,关闭加载的套接字库(closesocket() / WSACleanup());

编程步骤

1. 初始化 WSAStartup

    WSAStartup 必须是第一个 Windows 套接字函数。它允许指定 Windows 套接字版本,并检索特定 Windows 套接字实现的详细信息。

int WSAAPI WSAStartup(
  [in]  WORD      wVersionRequested, // 版本2.2 —— 0x0202 —— MAKEWORD(2,2)
  [out] LPWSADATA lpWSAData
);
  • [in] wVersionRequested

    调用方可以使用的最高版本的 Windows 套接字规范。 高顺序字节指定次要版本号;低顺序字节指定主版本号。

  • [out] lpWSAData

    指向 WSADATA 数据结构的指针,该结构用于接收 Windows 套接字实现的详细信息。

    如果 WSADATA 结构的 wVersion 成员对调用方不可接受的,则应用程序或 DLL 应调用 WSACleanup 来释放 Winsock DLL 资源,并且无法初始化 Winsock 应用程序。

2. 创建 socket

    创建的 socket 默认是一个主动类型的。

SOCKET socket(
    int domain, // 协议族
    int type, // socket 类型
    int protocol // 协议类型
);

2.1 协议族

    domain,指明通信域,决定了 socket 的地址类型。

名称 含义 名称 含义
PF_UNIX, PF_LOCAL 本地通信 PF_X25 ITU-T X25 / ISO-8208协议
AF_INET, PF_INET IPv4 Internet协议 PF_AX25 Amateur radio AX.25
PF_INET6 IPv6 Internet协议 PF_ATMPVC 原始ATM PVC访问
PF_IPX IPX-Novell协议 PF_APPLETALK Appletalk
PF_NETLINK 内核用户界面设备 PF_PACKET 底层包访问

2.2 socket 类型

    type,指定通信类型,常用的有 SOCK_STREAM、SOCK_DGRAM。

名称 含义
SOCK_STREAM TCP 连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输
SOCK_DGRAM 支持 UDP 连接(无连接状态的消息)
SOCK_SEQPACKET 序列化包,提供一个序列化的、可靠的、双向的基本连接的数据传输通道,数据长度定常。每次调用读系统调用时数据需要将全部数据读出
SOCK_RAW RAW 类型,提供原始网络协议访问
SOCK_RDM 提供可靠的数据报文,可能数据会有乱序
SOCK_PACKET 一个专用类型,不能在通用程序中使用

2.3 协议

    protocol,指定协议,对于 TCP 可指定为 IPPROTO_TCP,UDP 可为 IPPROTO_UDP。 

协议 名称
IPPROTO_TCP TCP 传输协议
IPPROTO_TCP UDP 传输协议
IPPROTO_SCTP STCP 传输协议
IPPROTO_TIPC TIPC 传输协议
0 默认对应 type 的协议

3. 绑定 bind (服务端)

int bind(
    SOCKET sockfd, // 创建的 socket
    const struct sockaddr_in *addr, // 通信协议地址
    socklen_t addrlen // 对应协议地址的长度
);

4. 监听 listen(服务端)

    将 socket 变为被动类型的,等待客户端的连接请求。

int listen(
    SOCKET sockfd, // 监听的 socket 描述字
    int backlog // 相应 socket 可排队的最大连接个数
);

5. 请求连接 connect(客户端)

int connect(
    SOCKET sockfd, // 连接的 socket 描述字
    const struct sockaddr_in *addr, // 通信协议地址 
    socklen_t addrlen // socket 地址长度
);

    通信协议地址赋值:

  • 赋值地址:

    • inet_pton(AF_INET, “127.0.0.1”, &addr.sin_addr);

    • addr.sin_addr.S_un.S_addr = inet_addr(“127.0.0.1”);

  • 赋值端口:

    • addr.sin_port = htons(8008);

6. 接收请求 accept(服务端)

SOCKET accept(
    SOCKET sockfd, // 服务器的 socket 描述字,监听 socket 描述字
    struct sockaddr_in *addr, // 通信协议地址
    socklen_t *addrlen // 协议地址的长度
);
  • 返回值是一个由内核自动生成的全新 socket 描述字,代表与返回客户端的 TCP 连接。

7. 发送数据 send(客户端)

int WSAAPI send(
  [in] SOCKET     sockfd, // 连接的 socket 描述字
  [in] const char *sendbuf, // 发送的多字节数据缓冲区
  [in] int        buflen, // 发送多字节长度
  [in] int        flags // 指定进行调用的方式
);

// Linux
// read()
  • buflen :

    • 大于发送缓冲区的长度,返回 SOCKET_ERROR;

    • 小于或等于时,send 先检查协议是否在发送 socket 数据,是则等待发送完,否则就进行比较 socket 发送缓冲区的剩余空间:

      • 大于剩余空间大小,则一直等待 socket 发送完;

      • 小于剩余空间大小,则将 sendbuf 数据 copy 到剩余空间中。

  • flags :

MSG_DONTROUTE 指定不应对数据进行路由。Windows 套接字服务提供商可以选择忽略此标志。
MSG_OOB 发送 OOB 数据(流样式套接字,例如仅SOCK_STREAM)。

8. 接收数据 recv(服务端)

int WSAAPI recv(
  [in] SOCKET     sockfd, // 标识连接的 socket 描述字
  [in] const char *recvbuf, // 接收的多字节数据缓冲区
  [in] int        buflen, // 接收的多字节长度
  [in] int        flags // 指定进行调用的方式
);

// Linux
// read()
  • recvbuf : 接收数据之前,必须 memset 进行清空,接收的数据不一定填满空间;

  • 返回值:

    • 未发生错误,则将返回接收到的字符数,recvbuf 指向的缓冲区将包含接收的数据;

    • 如果连接已正常关闭,则返回 0;

    • 否则返回 SOCKET_ERROR ,通过调用 WSAGetLastError 来检索特定的错误代码。

错误代码:recv 函数 (winsock2.h) - Win32 apps | Microsoft Learn

9. 关闭 closesocket

int closesocket(
  [in] SOCKET s
);
  • 返回:

    • 无异常,返回 0 ;

    • 否则将返回一个 SOCKET_ERROR 值:

      错误代码 意义
      WSANOTINITIALISED 未初始化调用 WSAStartup
      WSAENETDOWN 网络子系统出现故障
      WSAENOTSOCK 描述符不是套接字
      WSAEINPROGRESS 阻止 Windows 套接字 1.1 调用正在进行中,或者服务提供商仍在处理回调函数。
      WSAEINTR (阻止)Windows Socket 1.1 调用已通过 WSACancelBlockingCall 取消。
      WSAEWOULDBLOCK 套接字标记为非阻塞,但延迟结构的 l_onoff 成员设置为非零,l_linger 成员的延迟结构设置为非零超时值。

补充:close 标记 TCP socket 为已关闭,不可作为读写数据的第一个参数:

// Linux
#include <unistd.h>
int close(int fd);

注意:close 只是使 socket 描述字的引用计数 -1,当引用计数为 0 才会触发 TCP 客户端向服务器发送终止连接请求。

10. 停止使用 WSACleanup

int WSACleanup();
  • 返回值:

    • 无异常返回 0;

    • 否则,返回 SOCKET_ERROR 值,调用  WSAGetLastError 来检索特定的错误代码。

      错误代码 意义
      WSANOTINITIALISED* 未初始化调用 WSAStartup
      WSAENETDOWN 网络子系统出现故障
      WSAEINPROGRESS 阻止 Windows 套接字 1.1 调用正在进行中,或者服务提供商仍在处理回调函数。

实例

服务端

#include <stdio.h>  
#include <winsock2.h>  

#pragma comment(lib,"ws2_32.lib")  

int main(int argc, char* argv[])  
{  
    //初始化WSA  
    WORD sockVersion = MAKEWORD(2,2);  
    WSADATA wsaData;  
    if(WSAStartup(sockVersion, &wsaData)!=0)  
    {  
        return 0;  
    }  

    //创建套接字  
    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
    if(slisten == INVALID_SOCKET)  
    {  
        printf("socket error !");  
        return 0;  
    }  

    //绑定IP和端口  
    sockaddr_in sin;  
    sin.sin_family = AF_INET;  
    sin.sin_port = htons(8888);  
    sin.sin_addr.S_un.S_addr = INADDR_ANY;   
    if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)  
    {  
        printf("bind error !");  
    }  

    //开始监听  
    if(listen(slisten, 5) == SOCKET_ERROR)  
    {  
        printf("listen error !");  
        return 0;  
    }  

    //循环接收数据  
    SOCKET sClient;  
    sockaddr_in remoteAddr;  
    int nAddrlen = sizeof(remoteAddr);  
    char revData[255];   
    while (true)  
    {  
        printf("等待连接...\n");  
        sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);  
        if(sClient == INVALID_SOCKET)  
        {  
            printf("accept error !");  
            continue;  
        }  
        printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));  

        //接收数据  
        int ret = recv(sClient, revData, 255, 0);         
        if(ret > 0)  
        {  
            revData[ret] = 0x00;  
            printf(revData);  
        }  

        //发送数据  
        const char * sendData = "你好,TCP客户端!\n";  
        send(sClient, sendData, strlen(sendData), 0);  
        closesocket(sClient);  
    }  

    closesocket(slisten);  
    WSACleanup();  
    return 0;  
} 

客户端

#include<WINSOCK2.H>
#include<STDIO.H>
#include<iostream>
#include<cstring>
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main()
{
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA data;
    if(WSAStartup(sockVersion, &data)!=0)
    {
        return 0;
    }
    while(true){
        SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(sclient == INVALID_SOCKET)
        {
            printf("invalid socket!");
            return 0;
        }

        sockaddr_in serAddr;
        serAddr.sin_family = AF_INET;
        serAddr.sin_port = htons(8888);
        serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        if(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
        {  //连接失败 
            printf("connect error !");
            closesocket(sclient);
            return 0;
        }

        string data;
        cin>>data;
        const char * sendData;
        sendData = data.c_str();   //string转const char* 
        //char * sendData = "你好,TCP服务端,我是客户端\n";
        send(sclient, sendData, strlen(sendData), 0);
        //send()用来将数据由指定的socket传给对方主机
        //int send(int s, const void * msg, int len, unsigned int flags)
        //s为已建立好连接的socket,msg指向数据内容,len则为数据长度,参数flags一般设0
        //成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error 

        char recData[255];
        int ret = recv(sclient, recData, 255, 0);
        if(ret>0){
            recData[ret] = 0x00;
            printf(recData);
        } 
        closesocket(sclient);
    }

    WSACleanup();
    return 0;    
} 

异常

  • undefined reference to '_imp_WSAStartup‘

    解决方案:vs 属性工具 -> 编译选项 -> 链接器命令行 -> 命令添加 -lwsock32文章来源地址https://www.toymoban.com/news/detail-632550.html

到了这里,关于c++: websocket 客户端与服务端之间的连接交互的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++实现websocket服务端客户端(基于boost,亲测可行!)

       整篇文章基本参考了https://blog.csdn.net/jianghuan0122/article/details/123528907,文章记录了如何在现有条件下实现该参考示例(参考示例存在报错,并且参考示例没有介绍环境安装,正确源码附于文末)    自身环境:ubuntu18.04+gcc7.5.0+boost1.7,3   gcc或者g++一般都有,这里主要介绍

    2024年02月11日
    浏览(32)
  • 如何将本地websocket服务端从本地暴露至公网实现客户端远程连接

    1. Java 服务端demo环境 jdk1.8 框架:springboot+maven 工具IDEA 2. 在pom文件引入第三包封装的netty框架maven坐标 注意:pom文件里需注释掉springbootweb启动器,web启动器默认是tomcat服务启动,会和netty服务冲突 3. 创建服务端,以接口模式调用,方便外部调用 4. 启动服务,出现以下信息表示启动成功

    2024年04月10日
    浏览(36)
  • java后端使用websocket实现与客户端之间接收及发送消息

    客户端请求websocket接口,连接通道=》我这边业务成功客户端发消息=》客户端自动刷新。 接口:ws://localhost:8080/websocket/xx 经测试,成功 如果是线上服务器连接,则需要在nginx里配置websocket相关内容,再重启nginx,代码如下 本地连接的时候用的是ws://,因为是http链接,但是如果是

    2024年02月16日
    浏览(31)
  • WebSocket 实现长连接及通过WebSocket获取客户端IP

    WebSocket 是一种支持双向通讯的网络通信协议。 实现过程: 1 添加ServerEndpointExporter配置bean 2 实现过程 需求是通过WebSocket,建立长连接,并获取当前在线的人数。通过Websocket 不断发送消息,建立长连接,给Session续命。我是通过MAC地址,区分不同的设备,因为我的需求中需要一

    2024年02月09日
    浏览(42)
  • 【go】gorilla/websocket如何判断客户端强制断开连接

    当客户端因为某些问题异常关闭连接时,可以判断关闭连接的异常类型 通过调用websocket.IsCloseError或websocket.IsUnexpectedCloseError即可 其中github源码如下 异常类型如下

    2024年02月16日
    浏览(43)
  • 实现c++轻量级别websocket协议客户端

    因以前发过这个代码,但是一直没有整理,这次整理了一下,持续修改,主要是要使用在arm的linux上,发送接收的数据压缩成图片发送出去。 要达到轻量websocket 使用,必须要达到几个方面才能足够简单, 1、不用加入其他的库 2、只需要使用头文件包含就可以 3、跨平台 如果

    2024年02月12日
    浏览(30)
  • UE5- c++ websocket客户端写法

    ue5 c++ 实现socket客户端,读取服务端数据,并进行解析 {projectName}.Build.cs里增加 \\\"WebSockets\\\",\\\"JsonUtilities\\\", \\\"Json\\\"配置信息,最终输出如下: 说明,其中myue521是我的模块名,文件名为myue521.Build.cs。 新增的模块:\\\"WebSockets\\\",\\\"JsonUtilities\\\", \\\"Json\\\"。 准备实现自己的webscoket_client。具体操作

    2024年02月10日
    浏览(26)
  • Springboot 集成WebSocket作为客户端,含重连接功能,开箱即用

    使用演示 只需要init后调用sendMessage方法即可,做到开箱即用。内部封装了失败重连接、断线重连接等功能。 基于Springboot工程 引入websocket依赖 开箱即用的工具类

    2024年02月04日
    浏览(49)
  • 客户端和服务端信息交互模型

    什么是客户端和服务端? 客户端:可以向服务器发请求,并接收返回的内容进行处理 服务器端:能够接收客户端请求,并且把相关资源信息返回给客户端的 当用户在地址栏中输入网址,到最后看到页面,中间都经历了什么? 后面会详细解析每个步骤干的事 A: URI / URL /URN U

    2024年02月03日
    浏览(41)
  • SpringBoot+WebSocket实现服务端、客户端

    小编最近一直在使用springboot框架开发项目,毕竟现在很多公司都在采用此框架,之后小编也会陆续写关于springboot开发常用功能的文章。 什么场景下会要使用到websocket的呢? websocket主要功能就是实现网络通讯,比如说最经典的客服聊天窗口、您有新的消息通知,或者是项目与

    2024年02月13日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包