C++封装的基于WinSock2的TCP服务端、客户端
无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习。
只写好了TCP通信服务端,有空把客户端流程也封装一下。
先上主函数:
// main.cpp : 异想家sandeepin poi!
#include "stdafx.h"
#include <iostream>
extern int JTCPserver();
extern int JTCPclient();
int main()
{
JTCPserver(); // 两者选一
// JTCPclient();
return 0;
}
JTCPserver.cpp内容:
// JTCPserver.cpp : 蒋方正封装的TCP服务端
#include "stdafx.h"
#include <iostream>
#include <string>
#include <WinSock2.h> // WinSocket
#include <WS2tcpip.h> // IP地址转换用到inet_pton
#pragma comment(lib,"ws2_32.lib")
using namespace std;
// 【1】初始化WinSock
bool initWinSock();
// 【2】创建socket
bool createSocket(SOCKET &listenScok);
// 【3】socket绑定本机地址信息
bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port);
// 【4】侦听socket,接收客户端请求
bool listenSocket(SOCKET &listenScok);
// 【5】等待客户端连接-阻塞
bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock);
// 【6】接收数据-阻塞
bool receiveData(SOCKET &clientSock, string &data);
// 【7】停止套接字的接收、发送
bool shutdownSocket(SOCKET &clientSock);
// 【8】发送信息
bool sendData(SOCKET &clientSock, const string &data);
int JTCPserver()
{
SOCKET listenScok; // 服务端Socket
SOCKET clientSock; // 客户端Socket
string data; // 收到的数据
// 【1】初始化WinSock
initWinSock();
// 【2】创建socket
createSocket(listenScok);
// 【3】socket绑定本机地址信息
bindIPandPort(listenScok, "127.0.0.1", 1994);
// 【4】侦听socket,接收客户端请求
listenSocket(listenScok);
// 坐等客户端连接
bool isClientSockConnect = false; // 是否有客户端连进来
bool isReceiveData = false; // 是否接收数据成功
while (true)
{
if (!isClientSockConnect)
{
// 【5】等待客户端连接
isClientSockConnect = waitClientConnect(listenScok, clientSock);
}
else
{
if(!isReceiveData)
{
// 【6】接收数据-阻塞
isReceiveData = receiveData(clientSock, data);
// 如果接收数据失败则断开
if(!isReceiveData)
{
// 【7】停止套接字的接收、发送
shutdownSocket(clientSock);
cout << "等待客户端再连接..." << endl;
isClientSockConnect = false; // 可以重连了
}
}
if(isReceiveData && data != "jfzpoi" && data != "@end#")
{
isReceiveData = false;
}
if(isReceiveData && data == "jfzpoi")
{
// 【8】发送信息(收的数据为jfzpoi)
sendData(clientSock, "sandeepin!\r\n");
isReceiveData = false;
}
if (isReceiveData && data == "@end#")
{
// 【9】关闭相关
int err;
// err = shutdown(listenScok, 2);
// if (err == SOCKET_ERROR)
// {
// cout << "关闭失败!" << endl;
// }
// 关闭套接字,释放资源
err = closesocket(listenScok);
if (err == SOCKET_ERROR)
{
cout << "关闭socket失败!" << endl;
}
// 停止使用WinSock库,释放对应资源
if (WSACleanup() != 0)
{
cout << "WSA清空失败!" << endl;
}
cout << "关完了,坐等关机!poi" << endl;
return 0;
}
}
}
}
// 【1】初始化WinSock
bool initWinSock()
{
WORD verision = MAKEWORD(2, 2);
WSADATA lpData;
int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
if (intEr != 0)
{
cout << "WinSock初始化失败!" << endl;
return false;
}
cout << "WinSock初始化成功!" << endl;
return true;
}
// 【2】创建socket
bool createSocket(SOCKET &listenScok)
{
// 创建侦听socket
listenScok = socket(AF_INET, SOCK_STREAM, 0);
if (listenScok == INVALID_SOCKET)
{
cout << "socket创建失败!" << endl;
return false;
}
cout << "socket创建成功!" << endl;
return true;
}
// 【3】socket绑定本机地址信息
bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port)
{
// 制作sockaddr_in结构体
// 在bind函数,connect函数里提到了套接字编程网络地址信息结构体const struct sockaddr和const struct sockaddr_i
sockaddr_in hostAddr;
hostAddr.sin_family = AF_INET;
hostAddr.sin_port = htons(port);//转换成网络字节序
//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序
//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;
/*
inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
头文件:WS2tcpip.h
*/
in_addr addr;
inet_pton(AF_INET, ip.c_str(), (void*)&addr);
hostAddr.sin_addr = addr;
cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
cout << "ip(常规形式):" << ip.c_str() << endl;
// 侦听套接字listenSock绑定本机地址信息
int err = bind(listenScok, (struct sockaddr*)&hostAddr, sizeof(sockaddr));
if (err != 0)
{
cout << "本地IP绑定失败!" << endl;
return false;
}
return true;
}
// 【4】侦听socket,接收客户端请求
bool listenSocket(SOCKET &listenScok)
{
// 设定套接字为侦听状态,准备接收客户机进程发送来的连接请求
int err = listen(listenScok, 3);
if (err != 0)
{
cout << "socket监听失败!" << endl;
return false;
}
cout << "监听客户端连接中……" << endl;
return true;
}
// 【5】等待客户端连接-阻塞
bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock)
{
sockaddr_in clientAddr;
int len = sizeof(struct sockaddr); // 必须指定长度,否则会导致accept返回10014错误
// accept会循环等待客户端连接
clientSock = accept(listenScok, (struct sockaddr*)&clientAddr, &len);
cout << "客户端Socket编号:" << clientSock << endl;
if (clientSock == INVALID_SOCKET)
{
cout << "客户端连接失败!" << endl;
cout << WSAGetLastError() << endl;
return false;
}
return true;
}
// 【6】接收数据-阻塞
bool receiveData(SOCKET &clientSock, string &data)
{
static int cnt = 1; // 接收数据编号-静态
// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
char buf[1024] = "\0";
// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
int buflen = recv(clientSock, buf, 1024, 0);
if (buflen == SOCKET_ERROR)
{
cout << "接收失败!" << endl;
return false;
}
// 一切正常则显示接收数据
data = string(buf);
cout << "收到第" << cnt++ << "次,内容为:\n" << buf << endl;
return true;
}
// 【7】停止套接字的接收、发送
bool shutdownSocket(SOCKET &clientSock)
{
//(收完就关)停止套接字的接收、发送功能(0禁止接收,1禁止发送,2禁制接收发送)
int err = shutdown(clientSock, 2);
if (err == SOCKET_ERROR)
{
cout << "关闭Socket失败!" << endl;
return false;
}
return true;
}
// 【8】发送信息
bool sendData(SOCKET &clientSock, const string &data)
{
int err = send(clientSock, data.c_str(), data.size(), 0);
if (err == SOCKET_ERROR)
{
cout << "发送失败!" << endl;
return false;
}
cout << "发送数据为:\n" << data << endl;
return true;
}
JTCPclient.cpp内容:
// JTCPclient.cpp : 蒋方正封装的TCP客户端代码
#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
// 【1】初始化WinSock
bool initWinSockC();
// 【2】创建socket
bool createSocketC(SOCKET &listenScok);
// 【3】连接到服务器
bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port);
// 【4】发送数据
bool sendDataC(SOCKET &clientSock, const string &data);
// 【5】接收数据
bool receiveDataC(SOCKET &clientSock, string &data);
int JTCPclient()
{
SOCKET clientSock; // 客户端Socket
string data; // 收到的数据
bool isCreateSocket = false; // 是否创建了Socket
bool isConnectSocket = false; // 是否连上了服务器
bool isSendDataOK = false; // 是否发送成功数据
bool isReceiveDataOK = false; // 是否接收成功数据
// 【1】初始化WinSock
if(initWinSockC())
{
while (true)
{
if(!isCreateSocket)
{
// 【2】创建socket
createSocketC(clientSock);
isCreateSocket = true;
}
else
{
if(!isConnectSocket)
{
// 【3】连接到服务器
connectSocketC(clientSock, "127.0.0.1", 1994);
isConnectSocket = true;
}
else
{
if(!isSendDataOK)
{
// 【4】发送数据
isSendDataOK = sendDataC(clientSock, "jfz hello\r\n");
}
else
{
if(!isReceiveDataOK)
{
// 【5】接收数据
isReceiveDataOK = receiveDataC(clientSock, data);
}
else
{
if(data == "@end#")
{
WSACleanup();
return 0;
}
isReceiveDataOK = false;
isSendDataOK = false;
}
}
}
}
}
}
return 0;
}
// 【1】初始化WinSock
bool initWinSockC()
{
WORD verision = MAKEWORD(2, 2);
WSADATA lpData;
int intEr = WSAStartup(verision, &lpData); // 指定winsock版本并初始化
if (intEr != 0)
{
std::cout << "WinSock初始化失败!" << endl;
return false;
}
std::cout << "WinSock初始化成功!" << endl;
return true;
}
// 【2】创建socket
bool createSocketC(SOCKET &listenScok)
{
// 创建侦听socket
listenScok = socket(AF_INET, SOCK_STREAM, 0);
if (listenScok == INVALID_SOCKET)
{
cout << "socket创建失败!" << endl;
return false;
}
cout << "socket创建成功!" << endl;
return true;
}
// 【3】连接到服务器
bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port)
{
// 建立地址结构体
sockaddr_in hostAddr;
hostAddr.sin_family = AF_INET;
hostAddr.sin_port = htons(port);//转换成网络字节序
//hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//转换成网络字节序
//cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl;
/*
inet_addr()版本太低,被弃用使用inet_pton(协议族,字符串IP地址,void目标in_addr*)
头文件:WS2tcpip.h
*/
in_addr addr;
inet_pton(AF_INET, ip.c_str(), (void*)&addr);
hostAddr.sin_addr = addr;
cout << "ip(网络字节序):" << addr.S_un.S_addr << endl;
cout << "ip(常规形式):" << ip.c_str() << endl;
// 向服务器提出连接请求
int err = connect(conSock, (sockaddr*)&hostAddr, sizeof(sockaddr));
if (err == INVALID_SOCKET)
{
cout << "连接服务器失败!" << endl;
return false;
}
return true;
}
// 【4】发送数据
bool sendDataC(SOCKET &clientSock, const string &data)
{
int err = send(clientSock, data.c_str(), data.size(), 0);
if (err == SOCKET_ERROR)
{
cout << "发送失败!" << endl;
return false;
}
cout << "发送数据为:\n" << data.c_str() << endl;
return true;
}
// 【5】接收数据
bool receiveDataC(SOCKET &clientSock, string &data)
{
static int cnt = 1; // 接收数据编号-静态
// 通过已建立连接的套接字,接收数据 设定缓冲1024字节
char buf[1024] = "\0";
// flags操作方式(0正常数据,MSG_PEED系统缓冲区的数据复制到所提供的接收缓冲区内,系统缓冲区数据未删除,MSG_OOB处理带外数据,通常用参数0即可)
int buflen = recv(clientSock, buf, 1024, 0);
if (buflen == SOCKET_ERROR)
{
cout << "接收失败!" << endl;
return false;
}
// 一切正常则显示接收数据
data = string(buf);
cout << "收到第" << cnt++ << "次,内容为:\n" << buf << endl;
return true;
}
C++封装的基于WinSock2的TCP服务端、客户端的更多相关文章
- Mina TCP服务端客户端 示例
服务端代码: package com.xd.nms.example; import java.io.IOException; import java.net.InetSocketAddress; im ...
- .net for TCP服务端 && 客户端
关键代码 详细代码请看示例代码 Service //创建套接字 IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ipaddress), port); / ...
- [javaSE] 网络编程(TCP服务端客户端互访阻塞)
客户端给服务端发送数据,服务端收到数据后,给客户端反馈数据 客户端: 获取Socket对象,new出来,构造参数:String的ip地址,int的端口号 调用Socket对象的getOutputStr ...
- TCP/IP网络编程之基于UDP的服务端/客户端
理解UDP 在之前学习TCP的过程中,我们还了解了TCP/IP协议栈.在四层TCP/IP模型中,传输层分为TCP和UDP这两种.数据交换过程可以分为通过TCP套接字完成的TCP方式和通过UDP套接字完 ...
- 基于Select模型的Windows TCP服务端和客户端程序示例
最近跟着刘远东老师的<C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台)>,Bilibili视频地址为C++百万并发网络通信引擎架构与实现(服务端.客户端.跨平台),重新复习下 ...
- TCP/IP网络编程之基于TCP的服务端/客户端(二)
回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...
- TCP/IP网络编程之基于TCP的服务端/客户端(一)
理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流(stream)的套接字.TCP是Transmissi ...
- TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端
目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP部分的使用 框架源码结构 补充说明 源码地址 说明 之前有好几篇博客在讲TCP/UDP通信方 ...
- 【转】TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端)、UDP客户端
[转]TCP/UDP简易通信框架源码,支持轻松管理多个TCP服务端(客户端).UDP客户端 目录 说明 TCP/UDP通信主要结构 管理多个Socket的解决方案 框架中TCP部分的使用 框架中UDP ...
随机推荐
- 【Docker】删除镜像
删除镜像:docker rmi [OPTIONS] IMAGE [IMAGE...] 1.删除所有未被 tag 标记和未被容器使用的镜像: docker image prune 2.删除所有未被容器使 ...
- C++Review5_Swap交换
面试中可能会问到交换两个变量的值有几种实现方式,对这方面有一定了解还是有必要的,简单罗列一下几种方式,具体介绍查看参考链接: 1.中间变量:->这个最常见了 2.加减法: 3.异或法: 4.sw ...
- HDU1172猜数字 [模拟]
1.题意 任务是猜一个四位数,每次尝试后会给出这次猜中了几个数字和猜中了几个位置,求能否根据尝试的记录给出答案 2.分析 数据给出查询次数和每次查询的数及其有几个数和几个位置符合,值得注意的是,猜对的 ...
- Vijos1774 机器翻译 [模拟]
1.题意:给定一段长度为N个单词的文章(一个单词用一个非负整数表示),可以使用一个容量为M个元素的容器.你的任务是使用字典的帮助翻译文章,遇到一个单词,查询之后将此单词的释义放入容器中,下次遇到时若此 ...
- 为什么在做微服务设计的时候需要DDD?
记得之前在规划和设计微服务架构的时候,张队长给了我一个至今依然记忆深刻的提示:『你的设计蓝图里为什么没有看到DDD的影子呢?』 随着对充血模型的领域认知的加深,我越加感觉到DDD的重要性.但是DDD内 ...
- 【题解】ARC101F Robots and Exits(DP转格路+树状数组优化DP)
[题解]ARC101F Robots and Exits(DP转格路+树状数组优化DP) 先删去所有只能进入一个洞的机器人,这对答案没有贡献 考虑一个机器人只能进入两个洞,且真正的限制条件是操作的前缀 ...
- idea2020注册码永久激活(激活到2100年)
首先有图有真相: 资源链接: 链接:https://pan.baidu.com/s/1DPIllnyhc7H4qL2yQb0OvQ 提取码:lbjx 第一步:将bin目录下的三个文件拷贝到IDEA安装 ...
- JAVA的引用类型
一.强引用 JAVA默认的引用类型,强引用,是在我们的开发工作当中普遍存在的.如果一个对象具有强引用,当内存空间不足的时候,java虚拟机宁可抛出OOM异常,也不会回收它来释放内存.但是我们可以将对象 ...
- Django 链接MySQL及数据操作
Django 链接MySQL Django创建的项目自带的数据库是SQLite3,我们想要链接MySQL的话,需要更改settings.py中的配置 1.在MySQL中创建好数据库,Django项目不 ...
- LibreOJ 6278. 数列分块入门 2 题解
题目链接:https://loj.ac/problem/6278 题目描述 给出一个长为 \(n\) 的数列,以及 \(n\) 个操作,操作涉及区间加法,询问区间内小于某个值 \(x\) 的元素个数. ...