首页 > 其他分享 >14.4 Socket 双向数据通信

14.4 Socket 双向数据通信

时间:2023-10-14 10:06:01浏览次数:35  
标签:数据通信 14.4 return SOCKET sClient 线程 客户端 sin Socket

所谓双向数据传输指的是客户端与服务端之间可以无差异的实现数据交互,此类功能实现的核心原理是通过创建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 许可协议。转载请注明出处!

标签:数据通信,14.4,return,SOCKET,sClient,线程,客户端,sin,Socket
From: https://blog.51cto.com/lyshark/7858212

相关文章

  • 14.4 Socket 双向数据通信
    所谓双向数据传输指的是客户端与服务端之间可以无差异的实现数据交互,此类功能实现的核心原理是通过创建CreateThread()函数多线程分别接收和发送数据包,这样一旦套接字被建立则两者都可以异步发送消息,本章将实现简单的双向交互功能。首先我们需要封装两个函数,这里RecvFunction函数......
  • Websocket vs SSE(Server-Sent Events)
    定义Websockets和SSE(服务器发送事件)都能够将数据推送到浏览器,但它们不是竞争技术。Websockets连接既可以向浏览器发送数据,也可以从浏览器接收数据。可以使用websockets的应用程序的一个很好的例子是聊天应用程序。SSE连接只能向浏览器推送数据。在线股票报价或Twitter......
  • 高效网络通信技术揭秘,Socket原理与实践
    Socket(套接字)是一种在计算机网络中进行通信的抽象概念。它提供了一种编程接口,使得应用程序能够通过网络进行数据交换。Socket可以在不同的计算机上的进程之间建立连接,实现数据的传输和通信。Socket是一个端点,由IP地址和端口号组成。IP地址指示计算机的位置,而端口号则指定......
  • PHP 的 Websocket 客户端和服务器
     /*------------------------------------------------------*///--需要安装websocket,我用的是下面链接提供的//--https://github.com/Textalk/websocket-php/*------------------------------------------------------*///客户端require('vendor/autoload.php');$c......
  • 14.3 Socket 字符串分块传输
    首先为什么要实行分块传输字符串,一般而言Socket套接字最长发送的字节数为8192字节,如果发送的字节超出了此范围则后续部分会被自动截断,此时将字符串进行分块传输将显得格外重要,分块传输的关键在于封装实现一个字符串切割函数,将特定缓冲区内的字串动态切割成一个个小的子块,当切割结......
  • socket网络编程
    Socket网络编程一、计算机网络概述1、IP地址的概念IP地址就是标识网络中设备的一个地址,好比现实生活中的家庭住址。网络设备的效果图:2、IP地址的表现形式说明:IP地址分为两类:IPv4和IPv6IPv4是目前使用的IP地址IPv6是未来使用的IP地址IPv4是由点分十进制组成IPv6是......
  • Unix domain socket 简介
    原文:https://www.cnblogs.com/sparkdev/p/8359028.html Unixdomainsocket又叫IPC(inter-processcommunication进程间通信)socket,用于实现同一主机上的进程间通信。socket原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIXdomainsocket......
  • 关于c语言操作libwebsockets示例
    第一步,安装libwebsockets库,c语言编写的,默认安装引用库,配置相应的库及路径第二步:上代码main.h ////CreatedbyAdministratoron2020/5/1.// #ifndefMEDIA_MAIN_H#defineMEDIA_MAIN_H #define boolchar volatileintexit_sig=0; #defineMAX_PAYLOAD_SIZE 10......
  • 14.2 Socket 反向远程命令行
    在本节,我们将继续深入探讨套接字通信技术,并介绍一种常见的用法,实现反向远程命令执行功能。对于安全从业者而言,经常需要在远程主机上执行命令并获取执行结果。本节将介绍如何利用_popen()函数来启动命令行进程,并将输出通过套接字发送回服务端,从而实现远程命令执行的功能。在实现......
  • linux socket地址
    socket地址://socket地址其实是一个结构体,封装端口号和IP等信息。后面的socket相关的API需要使用到这个socket地址。//客户端  -->  服务器(IP  、Port)通用socket地址:socket网络编程接口中表示socket地址是结构体sockaddr,其定义如下:(IPv4......