14.4 Socket 双向数据通信

news2024/10/7 8:25:04

所谓双向数据传输指的是客户端与服务端之间可以无差异的实现数据交互,此类功能实现的核心原理是通过创建CreateThread()函数多线程分别接收和发送数据包,这样一旦套接字被建立则两者都可以异步发送消息,本章将实现简单的双向交互功能。

首先我们需要封装两个函数,这里RecvFunction函数用于接收数据,SendFunction函数则用于发送数据,这两段代码在服务端与客户端之间是一致的两者可被共用。

#include <iostream>
#include <Winsock2.h> 
#include <windows.h>
#pragma comment (lib, "ws2_32")
#define BUF_SIZE 6400

// 接收数据线程
DWORD WINAPI RecvFunction(LPVOID lpParam)
{
  SOCKET sClient = *(SOCKET*)lpParam;
  int retVal;
  char bufRecv[BUF_SIZE];
  memset(bufRecv, 0, sizeof(bufRecv));
  while (1)
  {
    retVal = recv(sClient, bufRecv, BUF_SIZE, 0);
    if (retVal == SOCKET_ERROR)
    {
      printf("返回错误 \n");
      break;
    }
    else
    {
      printf("收到服务器消息: %s \n", bufRecv);
    }
  }
  return 0;
}

// 发送数据线程
DWORD WINAPI SendFunction(LPVOID lpParam)
{
  SOCKET sClient = *(SOCKET*)lpParam;
  int retVal;
  char bufSend[BUF_SIZE];
  memset(bufSend, 0, sizeof(bufSend));
  while (1)
  {
    gets_s(bufSend);
    retVal = send(sClient, bufSend, strlen(bufSend) + sizeof(char), 0);
    if (retVal == SOCKET_ERROR)
    {
      printf("发送错误 \n");
      break;
    }
  }
  return 0;
}

14.4.1 服务端实现

对于服务端代码而言,一旦accept函数接收到有客户端连接后则自动将该sClient指针传输到子线程内执行,这样即可实现两者功能互不干扰。程序中通过使用CreateThread函数创建了两个线程来处理与客户端之间的发送和接收数据。将SendFunctionRecvFunction作为参数传递给线程,并与新的客户端套接字一起传递。线程存储在变量hThread1hThread2中。

int main(int argc, char* argv[])
{
  // 初始化套接字动态库
  WSADATA wsaData;
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    return 1;
  }

  //  创建服务段套接字
  SOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sServer == INVALID_SOCKET)
  {
    WSACleanup();
    return -1;
  }

  //  服务端地址
  sockaddr_in addrServ;
  addrServ.sin_family = AF_INET;
  addrServ.sin_port = htons(9999);
  addrServ.sin_addr.s_addr = htonl(INADDR_ANY);

  //  绑定套接字
  if (bind(sServer, (const struct sockaddr*)&addrServ, sizeof(addrServ)) == SOCKET_ERROR)
  {
    closesocket(sServer);
    WSACleanup();
    return -1;
  }

  //  监听套接字
  if (listen(sServer, 5) == SOCKET_ERROR)
  {
    closesocket(sServer);
    WSACleanup();
    return -1;
  }

  SOCKET sClient;
  sockaddr_in addrClient;
  int addrClientLen = sizeof(addrClient);

  // 接收数据
  sClient = accept(sServer, (sockaddr FAR*) & addrClient, &addrClientLen);
  if (sClient == INVALID_SOCKET)
  {
    closesocket(sServer);
    WSACleanup();
    return -1;
  }

  printf("接收客户端 IP:[%s] --> port:[%d] \n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));

  // 分配线程
  HANDLE hThread1, hThread2;

  hThread1 = CreateThread(NULL, NULL, SendFunction, (LPVOID*)&sClient, 0, 0);
  hThread2 = CreateThread(NULL, NULL, RecvFunction, (LPVOID*)&sClient, 0, 0);

  WaitForSingleObject(hThread1, INFINITE);
  WaitForSingleObject(hThread2, INFINITE);
  CloseHandle(hThread1);
  CloseHandle(hThread2);

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

14.4.2 客户端实现

客户端的实现与服务端保持一致,唯一的区别在于客户端通过connect()主动向服务端发送连接请求,只要有新的连接被建立则将通过CreateThread函数创建线程,SendFunctionRecvFunction函数分别用于发送与接收功能。

int main(int argc, char* argv[])
{
  WSADATA wsaData;

  // 初始化库
  if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
  {
    return 1;
  }

  //  服务器套接字
  SOCKET sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sHost == INVALID_SOCKET)
  {
    WSACleanup();
    return -1;
  }

  SOCKADDR_IN servAddr;
  servAddr.sin_family = AF_INET;
  servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
  servAddr.sin_port = htons(9999);

  //  连接服务器
  if (connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
  {
    closesocket(sHost);
    WSACleanup();
    return -1;
  }

  printf("连接到服务器 IP:[%s] --> port:[%d] \n", inet_ntoa(servAddr.sin_addr), ntohs(servAddr.sin_port));


  // 分别创建两个线程
  HANDLE hThread1, hThread2;

  hThread1 = CreateThread(NULL, NULL, SendFunction, (LPVOID)&sHost, 0, 0);
  hThread2 = CreateThread(NULL, NULL, RecvFunction, (LPVOID)&sHost, 0, 0);

  WaitForSingleObject(hThread1, INFINITE);
  WaitForSingleObject(hThread2, INFINITE);

  CloseHandle(hThread1);
  CloseHandle(hThread2);

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

编译并运行这两个程序,读者可自行测试,不论是在服务端还是客户端均可以实现双向数据通信功能,输出效果如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/9fda2a72.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

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

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

相关文章

2023年中国光纤传感器发展历程、需求量及行业市场规模分析[图]

光纤传感器是利用光纤作为媒介&#xff0c;将外界温度、应变等被测量转化为光纤中传输的光波的强度、相位、频率、波长、偏振态等光学信息的部件。光纤传感器由光源、入射光纤、出射光纤、光调制器、光探测器及解调器组成。其基本原理是将光源的光经入射光纤送入调制区&#xf…

嵌入式开发学习之STM32F407点亮LED及J-Link下载(二)

嵌入式开发学习之STM32F407点亮LED及J-Link下载&#xff08;二&#xff09; 开发涉及工具控制端口配置端口的设定与确认端口配置方法实现点亮LED程序下载与仿真 有工程实例&#xff0c;链接在最底部。 开发涉及工具 开发环境&#xff08;IDE&#xff09;&#xff1a;IAR-ARM8…

2023年中国尾气净化催化材料产量、需求量及行业市场规模分析[图]

尾气处理催化单元系由催化剂厂商将以催化材料和活性组分物质为主要组成的催化剂涂覆在蜂窝陶瓷或金属等载体上而成&#xff0c;催化剂则起到处理尾气中有害物质的各类化学反应的关键催化作用&#xff0c;主要由稀土材料、氧化铝材料、贵金属材料等催化材料和增强催化材料性能的…

华为云云耀云服务器L实例评测 | 实例评测使用之硬件参数评测:华为云云耀云服务器下的 Linux 网络监控神器 bmon

华为云云耀云服务器L实例评测 &#xff5c; 实例评测使用之硬件参数评测&#xff1a;华为云云耀云服务器下的 Linux 网络监控神器 bmon 介绍华为云云耀云服务器 华为云云耀云服务器 &#xff08;目前已经全新升级为 华为云云耀云服务器L实例&#xff09; 华为云云耀云服务器是什…

Ubuntu中不能使用ifconfig命令

​ 问题 打开终端使用如下命令不能运行&#xff1a; ifconfig显示如下错误: 解决方法 在VMware中的虚拟机下面打开“编辑虚拟机设置”&#xff0c;或者在已经打开的虚拟机面板上面打开“虚拟机—设置” 选择网络适配器&#xff0c;选择“NAT模式”&#xff0c;没开机的就…

如何使用C/C++刷新在终端上已经打印的内容

写本文的起源是因为在安装一些工具的时候&#xff0c;发现在终端上并行安装的情况下&#xff0c;显示安装信息是会修改之前已经打印出来的内容&#xff0c;这是怎么做到的呢&#xff1f;抱着对这个问题的好奇我进行了一些探索。 终端是如何运行的 首先是最关键的问题&#xf…

华为OD机考算法题:基站维修工程师

题目部分 题目基站维护工程师难度难题目说明题目说明小王是一名基站维护工程师&#xff0c;负责某区域的基站维护。 某地方有 n 个基站 ( 1 < n < 10 )&#xff0c;已知各基站之间的距离 s ( 0 < s < 500 )&#xff0c;并目基站 x 到基站 y 的距离&#xff0c;与基…

力扣第669题 修剪二叉搜索树 c++(注释)

题目 669. 修剪二叉搜索树 中等 相关标签 树 深度优先搜索 二叉搜索树 二叉树 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元…

实验室超声波清洗机有什么优点?

无论是在工业生产中&#xff0c;还是在日常生活中&#xff0c;清洁工作总是贯穿其中。特别是在社会加工过程中&#xff0c;必须清洗仪器和表面的工作量很大。此外&#xff0c;还有许多人力不能接触到需要清洗的部分&#xff0c;需要使用专业设备进行清洗工作。实验室超声波清洗…

【数值分析】0 - 数值分析绪论

文章目录 一、数值分析介绍二、数值分析应用2.1 解三角函数2.2 计算多项式2.3 解线性方程组2.4 供水计划和生产调度计划的制定2.5 湘江水流量估计的实际意义2.6 机器学习或大数据 三、数值分析内容四、数值分析参考书目 学习视频&#xff1a;《数值分析》| 华科 | 研究生基础课…

C++标准模板(STL)- 类型支持 (数值极限,max_digits10,radix,min_exponent)

数值极限 定义于头文件 <limits> 定义于头文件 <limits> template< class T > class numeric_limits; numeric_limits 类模板提供查询各种算术类型属性的标准化方式&#xff08;例如 int 类型的最大可能值是 std::numeric_limits<int>::max() &…

XXL-Job分布式任务调度框架-单机模式和分片模式执行任务4

一 调度模式分类 1.1 调度模式 1.单个任务&#xff1a;一个任务实例便可完成 a)单机单任务&#xff1a;单机模式下任何路由模式都只有一个实例执行 b)集群单任务&#xff1a;由路由策略(广播模式除外)选择其中一个实例完成 2.集群部署&#xff1a;每个实例都同时执行一部分…

RS232电路设计

学习一下RS232的电路设计 在学习过RS485电路设计后&#xff0c;对232就没那么陌生了。 下面我来说一下对于一个新手从0到1 的过程 首先我会查询关于RS232的相关只是&#xff0c;对他有初步了解&#xff0c;在485的基础上很好理解232了。 串口、COM口、UART口, TTL、RS-232、…

尚硅谷Flink(一)

目录 ☄️前置工作 fenfa脚本 &#x1f30b;概述 ☄️Flink是什么 ☄️特点&#xff08;多nb&#xff09; ☄️应用场景&#xff08;不用看&#xff09; ☄️分层API &#x1f30b;配环境 ☄️wordcount ☄️WcDemoUnboundStreaming &#x1f30b;集群部署 ☄️集…

机器视觉工程师为什么要反复调试?

目录 那么程序debug原因有哪些&#xff1f;图像处理debug的原因有哪些&#xff1f; 我们机器视觉项目的程序包含&#xff0c;业务逻辑图像处理&#xff0c;所以我们不单单调试图像处理部分&#xff0c;还要调试C#&#xff0c;界面&#xff0c;数据等等。我们必须保证程序稳定性…

手把手带你用Python和文心一言搭建《AI看图写诗》网页项目(附上完整项目源码)

今年年初&#xff0c;ChatGPT的火爆在全球掀起AI大模型的开发热潮&#xff0c;国内外的科技公司纷纷加入“百模大战”行列。百度在率先发布了国内第一款人工智能大语言模型“文心一言”后&#xff0c;又推出了文心千帆大模型平台&#xff0c;帮助企业和开发者加速大模型应用落地…

ARRI阿莱MXF(ALEXA Mini LF)多碎片重组案例

三大影视巨头ARRI、RED、ATOMOS&#xff0c;ARRI阿莱的设备以提供电影级的画质而在影视圈有很大的名气。ARRI的文件一般是两种&#xff0c;一种是高清MOV&#xff08;苹果为ARRI专门出过特殊认证的编码&#xff09;&#xff1b;一种是高清MXF&#xff1b;相比之下MXF文件比较少…

Vue之Jwt的运用(一起探索JWT在Vue中的用途吧)

目录 前言 一、JWT简介 1. 什么是JWT 2. JWT的工作原理 3. JWT的组成 4. 使用JWT的好处 二、JWT工具类 JwtFilter JwtUtils 工具类的作用 工具类的测试 测试代码 test01()测试结果 test02()测试结果 test03()测试结果 test04测试结果 test05测试结果 三、JWT集成SPA项…

Arya科普 | 程序员常用英语 Java常用名词解释 计算机常用英语汇总

本篇博客汇总了程序员常用的英文单词&#xff0c;Java中常用的名词解释&#xff0c;计算机常用的英语汇总。 英文 A~D array数组 accessible 可存取的 area面积 audio 音频 addition 加法 action 行动 arithmetic 算法 adjustment 调整 actual 真实的 argument 参量 ascent …

CVE-2017-7529 Nginx越界读取内存漏洞

漏洞概述 当使用Nginx标准模块时&#xff0c;攻击者可以通过发送包含恶意构造range域的header请求&#xff0c;来获取响应中的缓存文件头部信息。在某些配置中&#xff0c;缓存文件头可能包含后端服务器的IP地址或其它敏感信息&#xff0c;从而导致信息泄露。 影响版本 Ngin…