文章目录

  • 封装过程
    • 接口类的实现(抽象类)
      • _public_socket.h
      • TCP_INTERFACE.h(作用于win平台)
    • 服务器端封装
      • TCP_SOCKET_SERVER.h
      • TCP_SOCKET_SERVER.cpp
    • 客户端的封装
      • TCP_SOCKET_CLIENT.h
      • TCP_SOCKET_CLIENT.cpp
  • 实例讲解
    • 实例一:回声程序通信
      • 服务器回声程序
      • 客户端通信程序
      • 回声效果
    • 实例二:文件操作,传送图片(掌握重复传包)
      • 分析待传图片
      • 发送端程序
      • 接收端程序
      • 接收结果
  • 实例三:Web通信(浏览器访问服务器)
    • Web服务器程序
    • 接收结果
  • 总结

封装过程

接口类的实现(抽象类)

_public_socket.h

该头文件用于包含所有该系统平台socket所需要依赖的库。

  • windows平台
#ifndef MY_TINY_STL__PUBLIC_SOCKET_H
#define MY_TINY_STL__PUBLIC_SOCKET_H
#include <winsock2.h>
#pragma comment (lib, "ws2_32.lib")  //加载 ws2_32.dll
#endif //MY_TINY_STL__PUBLIC_SOCKET_H
  • Linux平台
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>

TCP_INTERFACE.h(作用于win平台)

由于该接口由服务器端和客户端继承,而两者同样的函数成员也就是这些了,设计客户端和服务器端时就只需要考虑各自的套接字以及其余操作的成员函数,也不需要管理DLL的开关。
还有一个erro_die()成员函数用于阻断错误并打印对应情况。

//
// Created by Alone on 2021/8/17.
//#ifndef MY_TINY_STL_TCP_INTERFACE_H
#define MY_TINY_STL_TCP_INTERFACE_H#include <cstdio>
#include "_public_socket.h"class TCP_INTERFACE {public:TCP_INTERFACE() {//初始化 DLLWSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);}//返回值小于等于0时发生错误virtual int Send(SOCKET clnt, const void *buf, const int buflen) = 0;virtual int Recv(SOCKET clnt, void *buf, const int buflen) = 0;//closesocket返回值不为0则发生错误virtual void Close(SOCKET clnt) = 0;virtual void error_die(const char *str) = 0;~TCP_INTERFACE() {WSACleanup();}};#endif //MY_TINY_STL_TCP_INTERFACE_H

服务器端封装

这次修改了下逻辑,还是用accept返回一个套接字进行发送和接收操作比较好。类的底层没有再保留用于和某个客户端通信的套接字了,自己控制各个客户端套接字的关闭和使用。

TCP_SOCKET_SERVER.h

//
// Created by Alone on 2021/8/16.
//#ifndef MY_TINY_STL_TCP_SOCKET_SERVER_H
#define MY_TINY_STL_TCP_SOCKET_SERVER_H#include "TCP_INTERFACE.h"class TCP_SOCKET_SERVER : public TCP_INTERFACE {public:TCP_SOCKET_SERVER();~TCP_SOCKET_SERVER();void Bind(int port);void Listen();SOCKET Accept();int Send(SOCKET clnt, const void *buf, const int buflen);int Recv(SOCKET clnt, void *buf, const int buflen);void Close(SOCKET clnt);void error_die(const char *str);private:SOCKET servSock;sockaddr_in sockAddr;};#endif //MY_TINY_STL_TCP_SOCKET_SERVER_H

TCP_SOCKET_SERVER.cpp

//
// Created by Alone on 2021/8/16.
//#include "TCP_SOCKET_SERVER.h"//初始化操作
TCP_SOCKET_SERVER::TCP_SOCKET_SERVER() : servSock(0) {memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
}//绑定操作
void TCP_SOCKET_SERVER::Bind(int port) {servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);sockAddr.sin_family = PF_INET;  //使用IPv4地址sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);  //具体的IP地址sockAddr.sin_port = htons(port);  //端口if (bind(servSock, (SOCKADDR *) &sockAddr, sizeof(SOCKADDR)) != 0) {error_die("bind");}
}//置于监听状态
void TCP_SOCKET_SERVER::Listen() {if (servSock == 0)error_die("listen");if (listen(servSock, SOMAXCONN) != 0) {error_die("listen");}
}//利用套接字的监听串口,接收客户端的请求,建立新的套接字进行存储信息
SOCKET TCP_SOCKET_SERVER::Accept() {SOCKADDR t;int nSize = sizeof(SOCKADDR);//后面两个参数为可选SOCKET clnt = accept(servSock, &t, &nSize);if (clnt <= 0)error_die("accept");return clnt;
}//返回的是发送到缓冲区的字节长度
int TCP_SOCKET_SERVER::Send(SOCKET clnt, const void *buf, const int buflen) {return send(clnt, (const char *) buf, buflen, 0);
}//返回已经接收的字节长度
int TCP_SOCKET_SERVER::Recv(SOCKET clnt, void *buf, const int buflen) {return recv(clnt, (char *) buf, buflen, 0);
}//析构函数关闭socket
TCP_SOCKET_SERVER::~TCP_SOCKET_SERVER() {if (servSock != 0)closesocket(servSock);
}void TCP_SOCKET_SERVER::Close(SOCKET clnt) {if (closesocket(clnt) != 0) {error_die("closesocket");}
}void TCP_SOCKET_SERVER::error_die(const char *str) {printf("[hint]%s failed:%d", str, WSAGetLastError());exit(-1);
}

客户端的封装

TCP_SOCKET_CLIENT.h

增加了利用域名查询ip地址的成员函数gethostbyname(),挺好玩的!此次增加了erro_die函数,且发送和接收都操作套接字。在类的内部还是保留了套接字的备份,用于忘记关闭套接字时,析构函数进行关闭。

//
// Created by Alone on 2021/8/18.
//#ifndef MY_TINY_STL_TCP_SOCKET_CLIENT_H
#define MY_TINY_STL_TCP_SOCKET_CLIENT_H#include "TCP_INTERFACE.h"
#include <iostream>class TCP_SOCKET_CLIENT : public TCP_INTERFACE {public:TCP_SOCKET_CLIENT();~TCP_SOCKET_CLIENT();SOCKET Connect(const char *IPAdrr, u_short port);//用于利用URL(域名)查询IP地址void Gethostbyname(const char *URL);//接口必须实现的函数int Send(SOCKET clnt,const void *buf, const int bufSize);int Recv(SOCKET clnt,void *buf, const int bufSize);void Close(SOCKET clnt);void error_die(const char *str);private://由于一般客户端只需要一个套接字实现连接,然后还需要一个socketadrr_in用于连接内容的赋值SOCKET clntSock;sockaddr_in sockAddr;
};#endif //MY_TINY_STL_TCP_SOCKET_CLIENT_H

TCP_SOCKET_CLIENT.cpp

//
// Created by Alone on 2021/8/17.
//#include "TCP_SOCKET_CLIENT.h"//初始化
TCP_SOCKET_CLIENT::TCP_SOCKET_CLIENT() : clntSock(0) {}//关闭套接字操作
void TCP_SOCKET_CLIENT::Close(SOCKET clnt) {if (closesocket(clnt) != 0)error_die("close");clntSock = 0;
}//连接服务器操作
SOCKET TCP_SOCKET_CLIENT::Connect(const char *IPAdrr, u_short port) {memset(&sockAddr, 0, sizeof sockAddr);clntSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);sockAddr.sin_family = PF_INET;sockAddr.sin_addr.s_addr = inet_addr(IPAdrr);sockAddr.sin_port = htons(port);if (connect(clntSock, (SOCKADDR *) &sockAddr, sizeof(sockAddr)) != 0) {error_die("connect");}return clntSock;
}//发送信息操作
int TCP_SOCKET_CLIENT::Send(SOCKET clnt,const void *buf, const int bufSize) {return send(clnt, (const char *) buf, bufSize, 0);
}//接收信息操作
int TCP_SOCKET_CLIENT::Recv(SOCKET clnt,void *buf, const int bufSize) {return recv(clnt, (char *) buf, bufSize, 0);
}//根据域名获取ip地址等信息
void TCP_SOCKET_CLIENT::Gethostbyname(const char *URL) {hostent *host = gethostbyname(URL);if (!host) {std::cout << "Get IP address error!\n";return;}//打印本命std::cout << URL << std::endl;//别名for (int i = 0; host->h_aliases[i]; i++) {printf("Aliases %d: %s\n", i + 1, host->h_aliases[i]);}//地址类型printf("Address type: %s\n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");//IP地址,其中inet_ntoa()函数是将网络字节序转为本地的字节序,方便打印看懂for (int i = 0; host->h_addr_list[i]; i++) {printf("IP addr %d: %s\n", i + 1, inet_ntoa(*(struct in_addr *) host->h_addr_list[i]));}
}//析构时需要确保所有东西已经关闭
TCP_SOCKET_CLIENT::~TCP_SOCKET_CLIENT() {if (clntSock != 0)closesocket(clntSock);
}void TCP_SOCKET_CLIENT::error_die(const char *str) {printf("[hint]%s failed:%d", str, WSAGetLastError());exit(-1);
}

实例讲解

实例一:回声程序通信

服务器回声程序

绑定本地1234端口,进入监听状态等待请求,如果通信对象关闭了通信,也不慌,重新goto到等待请求得到新的通信套接字

#include <iostream>
#include "TCP_SOCKET_SERVER.h"#define BUF_SIZE 1000
using namespace std;int main() {TCP_SOCKET_SERVER a;a.Bind(1234);a.Listen();restart:SOCKET clnt = a.Accept();while (1) {char *x = new char[BUF_SIZE];memset(x, 0, BUF_SIZE);int size = a.Recv(clnt,x, BUF_SIZE);if (size <= 0)break;if (a.Send(clnt,x, size) <= 0)break;}a.Close(clnt);cout << "connect is over.Waiting for a new connection!\n";goto restart;
}

客户端通信程序

为保持持续通信,一旦客户端拒绝了请求,那么弹出循环重新连接,并设置连接超时操作。

#include "TCP_SOCKET_CLIENT.h"
#define BUF_SIZE 100
int main(){TCP_SOCKET_CLIENT t;const char* to = "127.0.0.1";restart:SOCKET clnt = t.Connect(to,1234);while(1){std::cout<<"\nInput your message:\n";char buf[BUF_SIZE] = {0};std::cin.getline(buf,99);int size = t.Send(clnt,buf,BUF_SIZE);if(size<=0)break;memset(buf,0,sizeof buf);if(t.Recv(clnt,buf,size)<=0)break;printf("received from %s is:\n",to);std::cout<<buf;}t.Close(clnt);std::cout<<"The Server is disconnected,and socket has been cleaned up,socket connection has been re-established\n";goto restart;return 0;
}

回声效果

客户端收到的结果

服务器端一直运行着,只要不关闭,但每次只能和一个客户端进行通信,通信完后重新等待连接。

实例二:文件操作,传送图片(掌握重复传包)

分析待传图片

看看这百万大小的字节,一次肯定是传不完的,所以我们需要发送端不断的续传,直到传送完毕。

发送端程序

#include "TCP_SOCKET_CLIENT.h"
#include <fstream>
int main(){TCP_SOCKET_CLIENT t;const char* to = "127.0.0.1";restart:SOCKET clnt = t.Connect(to,1234);//图片写入buf(这几百万字节大小,得亏是new动态分配std::ifstream img("D:/DesktopBackground/L-69.png",std::ios::in|std::ios::binary);//设置文件指针用于求文件内容长度img.seekg(0,std::ios::end);int len = img.tellg();img.seekg(0,std::ios::beg);if(len>0){printf("read OK\n");}else {printf("file is empty!");return 0;}//填补bufchar * buf = new char[len];img.read(buf,len);//发送数据到服务器,一次肯定发送不完,所以多次int sum = 0;while(sum<len){int sendlen = t.Send(clnt,buf,len);if(sendlen<=0){printf("Send Erro!");return 0;}sum += sendlen;}t.Close(clnt);printf("Send OK!");return 0;
}

接收端程序

#include <iostream>
#include "TCP_SOCKET_SERVER.h"
#include <fstream>
#define BUF_SIZE 100
using namespace std;int main() {TCP_SOCKET_SERVER a;a.Bind(1234);a.Listen();//等待连接,连接成功便可建立通讯SOCKET clnt = a.Accept();//创建文件用于写入图片数据ofstream t("test.png",ios::binary|ios::out);//由于要接收的图片文件较大,需要分多次包进行传输数据,所以需要不断循环接收while(1){char buf[BUF_SIZE];int sz = a.Recv(clnt,buf,BUF_SIZE);//直到发送端发送数据完毕断开连接后,便可判断为接收完毕if(sz<=0){cout<<"Finish !";return 0;}//每次调整文件指针位置到最后续写t.seekp(0,ios::end);t.write(buf,sz);}a.Close(clnt);}

接收结果

一模一样毫无偏差

实例三:Web通信(浏览器访问服务器)

Web服务器程序

我这个web服务器也算是及其简单了。。并没有对客户端的http请求进行解析然后发送对应的文件给客户端,而是单纯的我客户端想怎么发就怎么发。。另外这个程序虽然是对图片进行了判断,但并未写出对应的图片发送程序(二进制文件读写是不一样的),所以实际只能发送文本文件,如html代码。所以后面看到的课程表都无法显示图片的原因是客户端程序压根就没在乎过客户端的请求

Socket基本操作的C++封装--以及Socket通信实践相关推荐

  1. 平台开发——skynet——④socket通信、以及高度封装的socket.channel

    本文目录 预备知识: socket服务端 socket客户端 socket.channel模式(针对客户端) 用 socket.channel 解决问题① 用 socket.channel 解决问题② ...

  2. C# Socket系列二 简单的创建 socket 通信

    看了系列一 我们开启了对socket tcp的监听状态,那么这一章我们来讲解怎么创建socket的通信代码 我新建一个类 TSocketBase public abstract class TSock ...

  3. Linux网络编程 | Socket编程(一):Socket的介绍、UDPSocket的封装、UDP服务器/客户端的实现

    目录 套接字编程 Sockaddr结构 字节序 地址转换 常用套接字接口 UDP的通信流程 UDPSocket的封装 UDP服务器 UDP客户端 套接字编程 所谓套接字(Socket),就是对网络中不 ...

  4. php winform通信,C# Winform 通过Socket实现客户端和服务端TCP通信

    操作界面如下: 1.声明Socket 第一个参数:寻址方式,第二个参数:传输数据的方式,第三个参数:通信协议 Socket socket = new Socket(AddressFamily.Inte ...

  5. linux socket ip层配置,Linux下Socket通信(TCP实现)

    近期在做的项目中,涉及到了进程间数据传输,系统的原本实现是通过管道,但是原有的实现中两个进程是在同一台机器,而且两个进程的关系为父子关系,而我们要做的是将其中一个进程移植到服务器上,因此两个进程要分开 ...

  6. SpringBoot(23) 集成socket.io服务端和客户端实现通信

    一.前言 websocket和socket.io区别? websocket 一种让客户端和服务器之间能进行双向实时通信的技术 使用时,虽然主流浏览器都已经支持,但仍然可能有不兼容的情况 适合用于cli ...

  7. 封装网络通信 socket 服务端和客户端

    文章目录 1.C++封装的socket 服务端 1.1 代码示例 1.2 代码解析 1.2.1 成员变量 1.2.2 成员函数 1.2.3 构造函数和析构函数 1.2.4 服务端类解析 2.C++封装 ...

  8. 利用java socket和sampled实现点对点即时语音通信

    ### 利用java socket和sampled实现点对点即时语音通信 基本思路 ​ 利用javax.sound.sampled 包中的方法可以很方便的实现,获取拾音器音频输入的内容,和写入音频输出 ...

  9. socket不能bind请求的地址_socket通信原理

    一.什么是Socket? 1.Socket是一个网络通信的套接字(接口) 二.Socket的实现流程? 1.Socket在客户端和服务端发生了什么? 服务器:a.socket()创建socket对象b ...

最新文章

  1. DeepSpeed超大规模模型训练工具
  2. 程序猿工作效率的影响因素和管理者怎样推断
  3. 用js改变embed标签的src值
  4. Spring的jdbcTemplate 与原始jdbc 整合c3p0的DBUtils 及Hibernate 对比 Spring配置文件生成约束的菜单方法...
  5. 1. Linux内核的配置与裁减:
  6. mysql指数函数_SQL语言参考大全的目录
  7. TCP连接——三次握手和四次断开
  8. 通过JS和CSS,实现网页加载中的动画效果
  9. oracle中sequence详解
  10. 建筑设计 - 概念设计 - 笔记:- 第一章
  11. 冬小麦病虫害的高光谱识别方法研究
  12. Lingo基础语法笔记
  13. 中断扫描方式子程序c语言,单片机原理及应用(C语言版) 第9章 节 单片机系统配置及接口.ppt...
  14. 【181012】VC++ 中国象棋人机对弈程序源代码
  15. 搜索实现vue+Element-ui仿商城搜索
  16. 今日凌晨Vue3 beta版震撼发布,竟然公开支持脚手架项目!
  17. AN蜘蛛池V2.0附搜索留痕转码工具SEO工具
  18. 【微信公众号开发】获取用户信息时,有时成功获取,有时提示“invalid openid hint”
  19. 当使用maven的clear功能导致数据库链接出现Using a password on the command line interface can be insecure.的解决方案
  20. Arduino Uno 使用MAX7219驱动单个8*8点阵LED模块

热门文章

  1. js onmousemove使用注意:进入onmousemove不代表移动鼠标
  2. StarRocks Parser 源码解析
  3. 【BLE】CC2541与CC2540的区别
  4. [统计学笔记] (十)一元线性回归
  5. 认知,构建个人的知识体系(上)
  6. c语言ifelse顺序,编程if语句属于顺序结构设计
  7. 那些名站的网站关键词都是如何到百度首页的
  8. 【智利支付】智利外贸收款方式Servipag
  9. 宇宙最强IDE!微软正式宣布 Visual Studio 2019
  10. 浅拷贝和深拷贝(谈谈java中的clone)