IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Socket基本操作的C++封装--以及Socket通信实践 -> 正文阅读

[C++知识库]Socket基本操作的C++封装--以及Socket通信实践

接口类的实现(抽象类)

_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的开关。

//
// Created by Alone on 2021/8/17.
//

#ifndef MY_TINY_STL_TCP_INTERFACE_H
#define MY_TINY_STL_TCP_INTERFACE_H
#include "_public_socket.h"

class TCP_INTERFACE {
public:
    TCP_INTERFACE(){
        //初始化 DLL
        WSADATA wsaData;
        WSAStartup( MAKEWORD(2, 2), &wsaData);
    }
    //返回值小于等于0时发生错误
    virtual int Send(const void*buf,const int buflen) = 0;
    virtual int Recv(void*buf,const int buflen) = 0;
    //closesocket返回值不为0则发生错误
    virtual bool Close() = 0;
    ~TCP_INTERFACE(){
        WSACleanup();
    }

};


#endif //MY_TINY_STL_TCP_INTERFACE_H

服务器端封装

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();
    bool Bind(int port);
    bool Listen();
    bool Accept();
    int Send(const void*buf,const int buflen);
    int Recv(void* buf,const int buflen);
    bool Close();
private:
//绑定本地端口的套接字servSock以及用关于构建通信对象的clntSock
//建立服务后的serve一般不会进行改变,所以close只提供为clntSock关闭
    SOCKET servSock;
    SOCKET clntSock;
    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"
//初始化操作两个socket为0表示还未分配套接字
TCP_SOCKET_SERVER::TCP_SOCKET_SERVER(): servSock(0), clntSock(0) {
    memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充

}
//析构函数所有关闭socket
TCP_SOCKET_SERVER::~TCP_SOCKET_SERVER() {
    if(clntSock!=0)closesocket(clntSock);
    if(servSock!=0)closesocket(servSock);
}
//绑定操作
bool TCP_SOCKET_SERVER::Bind(int port) {
    servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    sockAddr.sin_family = PF_INET;  //使用IPv4地址
    //INADDR_ANY代表本地0.0.0.0地址的整形数据
    sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);  //具体的IP地址
    sockAddr.sin_port = htons(port);  //端口
    if(bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR))!=0){
        closesocket(servSock);
        servSock = 0;
        return false;
    }
    return true;
}
//置于监听状态
bool TCP_SOCKET_SERVER::Listen() {
    if(servSock==0)
        return false;
    if(listen(servSock,20)!=0){
        closesocket(servSock);
        servSock = 0;
        return false;
    }

    return true;
}
//利用套接字的监听串口,接收客户端的请求,建立新的套接字进行存储信息
bool TCP_SOCKET_SERVER::Accept() {
    SOCKADDR t;int nSize = sizeof(SOCKADDR);
    //后面两个参数为可选(可以填入0,0)
    clntSock = accept(servSock,&t,&nSize);
    if(clntSock<=0){clntSock=0; return false};
    return true;
}
//返回的是发送到缓冲区的字节长度,小于等于0时表示出错
int TCP_SOCKET_SERVER::Send(const void *buf, const int buflen) {
    return send(clntSock,(const char*)buf,buflen,0);

}
//返回已经接收的字节长度,同上
int TCP_SOCKET_SERVER::Recv(void *buf, const int buflen) {
    return recv(clntSock,(char*)buf,buflen,0);
}
//closesocket返回值不等于0则出错
bool TCP_SOCKET_SERVER::Close() {
    if(clntSock==0)
        return false;
   if( closesocket(clntSock)!=0){
       return false;
   }clntSock = 0;
   return true;
}

客户端的封装

TCP_SOCKET_CLIENT.h

增加了利用域名查询ip地址的成员函数gethostbyname(),挺好玩的!

//
// Created by Alone on 2021/8/17.
//

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


#endif //PRACTICE_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) {
memset(&sockAddr,0,sizeof sockAddr);
}
//关闭套接字操作
bool TCP_SOCKET_CLIENT::Close() {
    if(clntSock==0)
        return false;
    if(closesocket(clntSock)!=0)
        return false;
    clntSock = 0;
    return true;
}
//连接服务器操作
bool TCP_SOCKET_CLIENT::Connect(const char *IPAdrr, u_short port) {
    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){
        closesocket(clntSock);
        clntSock = 0;
        return false;
    }
    return true;
}
//发送信息操作
int TCP_SOCKET_CLIENT::Send(const void *buf, const int bufSize) {
    return send(clntSock,(const char*)buf,bufSize,0);
}
//接收信息操作
int TCP_SOCKET_CLIENT::Recv(void *buf, const int bufSize) {
    return recv(clntSock,(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);
}

实例一:回声程序通信

服务器回声程序

绑定本地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:
    a.Accept();
    while (1) {
        char *x = new char[BUF_SIZE];
        memset(x, 0, BUF_SIZE);
        int size = a.Recv(x, BUF_SIZE);
        if (size <= 0)
            break;
        if (a.Send(x, size) <= 0)
            break;
    }
    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:
    if(!t.Connect(to,1234)){
        std::cout<<"connected timeout!";
        return 0;
    }
    while(1){
        std::cout<<"\nInput your message:\n";
        char buf[BUF_SIZE] = {0};
        std::cin.getline(buf,99);
        int size = t.Send(buf,BUF_SIZE);
        if(size<=0)
            break;
        memset(buf,0,sizeof buf);
        if(t.Recv(buf,size)<=0)
            break;
        printf("received from %s is:\n",to);
        std::cout<<buf;
    }
    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:
    if(!t.Connect(to,1234)){
        std::cout<<"connected timeout!";
        return 0;
    }
    //图片写入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;}
        //填补buf
    char * buf = new char[len];
    img.read(buf,len);
    //发送数据到服务器,一次肯定发送不完,所以多次
    int sum = 0;
    while(sum<len){
        int sendlen = t.Send(buf,len);
        if(sendlen<=0){
            printf("Send Erro!");
            return 0;
        }
        sum += sendlen;
    }

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

}

接收结果

一模一样毫无偏差

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-18 12:31:35  更:2021-08-18 12:31:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 6:44:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码