14.4 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函数创建了两个线程来处理与客户端之间的发送和接收数据。将SendFunction和RecvFunction作为参数传递给线程,并与新的客户端套接字一起传递。线程存储在变量hThread1和hThread2中。
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函数创建线程,SendFunction与RecvFunction函数分别用于发送与接收功能。
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;
}
编译并运行这两个程序,读者可自行测试,不论是在服务端还是客户端均可以实现双向数据通信功能,输出效果如下图所示;

14.4 Socket 双向数据通信的更多相关文章
- java中关于SSL/TSL的介绍和如何实现SSL Socket双向认证
一. SSL概述 SSL协议采用数字证书及数字签名进行双端实体认证,用非对称加密算法进行密钥协商,用对称加密算法将数据加密后进行传输以保证数据的保密性,并且通过计算数字摘要来验证数据在传 ...
- 《精通android网络开发》--使用Socket实现数据通信
No1: 网络传输应用通常使用TCP.IP或UDP这三种协议实现数据传输.在传输数据的过程中,需要通过一个双向的通信连接实现数据的交互.在这个传输过程中,通常将这个双向链路的一端称为Socket,一个 ...
- unity3D中使用Socket进行数据通信(二)
上一篇博客主要介绍了使用socket搭建服务端和client程序,这一篇来说说socket的数据传输,我们使用socket的目的是解决点对点之间的数据传输,之前提到了socket中一个重要的概念:po ...
- unity3D中使用Socket进行数据通信(一)
公司今年3D产品的工作中心主要集中在提高产品深度上,通过对竞争产品的分析,发现我们的缺陷在于多人在线与后台管理部分,多人在线使用unity自带的Network能够搞定,后台部分前段时间主要研究了下Sq ...
- (转)python标准库中socket模块详解
python标准库中socket模块详解 socket模块简介 原文:http://www.lybbn.cn/data/datas.php?yw=71 网络上的两个程序通过一个双向的通信连接实现数据的 ...
- Socket类的用法
原文:http://www.cnblogs.com/Elijah/archive/2011/11/29/2268047.html Socket可以理解成一个IP地址加一个端口,构成的一个“插座”... ...
- 轮询以及webSocket与socket.io原理
概述: 首先,我们知道,起初的http协议只是为了能够进行通信而被创造出来(也就是请求-响应的过程).并没有双向通信这一说,后面随着历史业务的需求,人们使用轮询http来解决双向通信也就是使用xhr或 ...
- 在web浏览器上显示室内温度(nodeJs+arduino+socket.io)
上次的nodejs操作arduino入门篇中实现了如何连接arduino.这次我们来实现通过arduino测量室内温度并在浏览器上显示出来. [所需材料] 硬件:LM35温度传感器,arduino u ...
- python异常处理、反射、socket
一.isinstance 判断对象是否为类的实例 n1 = print isinstance(n1,int) class A: pass class B(A): pass b= B() print i ...
- 使用socket.io开发简单群聊功能
1.新建package.json文件: { "name": "socket-chat-example", "version": " ...
随机推荐
- 深入了解 ReadDirectoryChangesW 并应用其监控文件目录
简介 监视指定目录的更改,并将有关更改的信息打印到控制台,该功能的实现不仅可以在内核层,在应用层同样可以.程序中使用 ReadDirectoryChangesW 函数来监视目录中的更改,并使用 FIL ...
- leaflet 绘制 点 线 面 圆 椭圆 线缓冲区
leaflet有个绘图插件Leaflet.draw,但是我不想要它的控件,只想用它的绘制功能,控件我自己提供,当时不知道如何使用,就自己写了个绘制点线面圆和椭圆的工具,代码如下: /// <re ...
- 深挖 Python 元组 pt.2
哈喽大家好,我是咸鱼 在<深挖 Python 元组 pt.1>中我们了解 Python 元组的一些概念(索引和切片等),以及如何创建元组,最重要的是我们还介绍了元组的不可变特性 那么今天我 ...
- AtCoder Beginner Contest 188 题解
AtCoder Beginner Contest 188 A,B很简单就不多说 C - ABC Tournament 找出前一半的最大值和后一半的最大值,二者中较小的那一个对应的序号就是最后的答案. ...
- 0x02 基本算法-枚举、模拟、递推
递归实现指数型枚举 int _, n, m, k, x, y; vector<int> vec; void calc(int x) { if (x == n + 1) { for (int ...
- 深入浅出 ZooKeeper
ZooKeeper 是一个分布式协调服务 ,由 Apache 进行维护. ZooKeeper 可以视为一个高可用的文件系统. ZooKeeper 可以用于发布/订阅.负载均衡.命令服务.分布式协调/通 ...
- python代码打包exe程序
1.安装pyinstaller 命令行输入:pip install pyinstaller 2.打包exe程序 输入命令:pyinstaller -F -w *.py(星号是.py的全部路径) pyi ...
- 【scikit-learn基础】--『监督学习』之 空间聚类
空间聚类算法是数据挖掘和机器学习领域中的一种重要技术. 本篇介绍的基于密度的空间聚类算法的概念可以追溯到1990年代初期.随着数据量的增长和数据维度的增加,基于密度的算法逐渐引起了研究者的关注.其中, ...
- Java开发者的Golang进修指南:从0->1带你实现协程池
在Java编程中,为了降低开销和优化程序的效率,我们常常使用线程池来管理线程的创建和销毁,并尽量复用已创建的对象.这样做不仅可以提高程序的运行效率,还能减少垃圾回收器对对象的回收次数. 在Golang ...
- Docker导出镜像的总结
Docker导出镜像的总结 安装Docker mkdir -p /etc/docker cat >/etc/docker/daemon.josn <<EOF { "bip& ...