Linux 下使用C++面向对象实现socket通信

0X00 什么是Socket通信

我们知道,在网络编程中,socket通信是入门级编程。就像是你学一门语言,就先用这门语言写出Hello World。所以我们今天就先写出这Hello World

Socket通信,也就是我们所说的套接字,这是进程间的一种通信方式,但与其它通信方式不一样的是,套接字可以通过网络进行传输。

我们知道的是,Socket通信是一种通信。通信通信,总该有两个对象才能进行相互的通信,才能叫通信。而在Socket通信中,这两端就是:Server(服务器)Client(客户端)

所以要实现socket通信,就是要解决这两端的编码

0X10 怎么进行Socket通信

我们刚才说了,我们需要对两端进行编码。因此,要知道怎么通信,就得分开来讲

1. Server端
对于服务器端,我们需要进行一下三部操作

第一步,先对Socket进行初始化,定义相应的协议与类型:

listenfd = socket(AF_INET, SOCK_STREAM, 0);

第二步,绑定响应的IP与Port端口:

bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr))

第三步,监听:

listen(listenfd, maxsize)

通过这三步,相当于服务器就开始工作了,端口有数据来时,就可以捕捉,这样我们就得到了来自客户端的数据。

这就是Server端的工作

2. Client端

客户端相对于服务器端就简单一点

首先也是对Socket进行初始化,也就是创建一个套接字

sockfd = socket(AF_INET, SOCK_STREAM, 0)

然后输入服务器的IP地址,再连接(connet),就可以进行相应的通信了:

connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr))

0X20 类的设计

我们这里是面向对象的编程,因为,我们需要设计相对应的类。

在这里我们很明确,只要设计两个类即可:Server 以及 Client

1. Server 类

首先,我们需要想清楚,server类中需要什么?

私有成员:

我们刚才看到了前面的代码中有listenfd 这个变量,他是在创建套接字是返回的值,因为在Linux中万物皆文件,因此套接字也是一个文件。
因此在调用socket()函数数调用成功后,会返回一个标识这个套接字的文件描述符,失败的时候返回-1(关于socket函数,可以看这里)
因此我们需要一个 listenfd 作为私有成员变量

还有后面我们需要用到的一个connfd变量,他是accept 函数调用成功后的返回值,和之前listenfd 一样,也是一个文件描述符。

要进行socket通信,还有就是一个很重要的变量就是 sockaddr_in 类型的变量,sockaddr_in是用来处理网络通信的地址

我们还需要一个 char* buff 来存储通信的信息,作为缓存区

当然,还需要port来存储我们用来进行通信的端口,以及存储最大连接数的 maxsize

私有成员差不多就这样了

公共成员:
公共成员中更多的是向外界提供的接口,也就是函数。

通过以上的分析,我们知道服务器最关键的三步,因此我们需要封装这三步操作,分别是:Socket() Bind() Listen()

然后我们再用一个start()函数来封装这三种操作

现在我们只是建立好了连接,准备通信,因此我们需要一个Handle()函数,其中封装了我们需要的操作

最后,需要关闭连接,也就是end()函数

好了,分析就差不多了,下面是完整的代码:

server.h

#ifndef   SERVER_H
#define   SERVER_H
#include "unified.h"

class Server
{
    
    
    private:
        int listenfd;
        int connfd;
        struct sockaddr_in  servaddr;
        char*  buff;
        int port;
        int maxsize;
    
        void Error(const char*);


    public:
        Server(int Po=8000,int Size=50):port(Po),maxsize(Size){
    
    buff = new char[MAXLINE];};

        void Socket();
        void Bind();
        void Listen();
        void Handle();
        void start();
        void stop(){
    
    close(listenfd);};

        ~Server(){
    
    delete [] buff;}

};

#endif

2. Client 类
知道了上面Server类的设计之后,Client的设计就差不多啦,这里就直接给代码了:

client.h

#ifndef   CLIENT_H
#define   CLIENT_H
#include "unified.h"

class Client
{
    
    
    private:
        int sockfd;
        char* sendline;	//发送的内容
        char* ip;
        int port;
        struct sockaddr_in  servaddr;

         void Error(const char*);
    
    public:
        Client(char* IP,int Po=8000):ip(IP),port(Po)
        {
    
    
            sendline = new char [MAXLINE];
        }

        void Socket();	//建立socket
        void Connet();	//连接
        void handle();	//处理
        void start();	//启动
        void stop(){
    
    close(sockfd);}

        ~Client()
        {
    
    
            delete [] sendline;
        }
};
#endif

0X30 类的实现

可能到了现在,大家还是比较懵的,因为只有类的定义,没有实现,还是不知道怎么做

现在我们看这些关键的实现,为了看起来简洁,因此直接在代码上注释了

——————————分———————————————割———————————————线————————
server.cc

#include "server.h"

void Server::Error(const char* message)
{
    
    
    printf(message,strerror(errno),errno);
     exit(0);
}

void Server::Socket()
{
    
    
    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )	//初始化套接字,AF_INET表示协议族,SOCK_STREAM是采用TCP协议
        Error("create socket error: %s(errno: %d)\n");// 返回值==-1,报错

    memset(&servaddr, 0, sizeof(servaddr));//将servaddr结构体的内容置0
    servaddr.sin_family = AF_INET;//规定协议族为SOCK_STREAM
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//这里表示的是需要监听的IP地址,也就是接受哪些IP发过来的数据包,INADDR_ANY任意,即0.0.0.0  如果是本地通信,那么就是127.0.0.1
    servaddr.sin_port = htons(port); //绑定端口
}

void Server::Bind()
{
    
    
     if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) //参数分别是之前的文件描述符,网络通信地址及其大小
            Error("bind socket error: %s(errno: %d)\n");
}

void Server::Listen()
{
    
    

    if( listen(listenfd, maxsize) == -1)//文件描述符以及最大连接数
         Error("listen socket error: %s(errno: %d)\n");
}

void Server::Handle()
{
    
    
     printf("======waiting for client's request======\n");
    while(1){
    
    
        if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
    
    	//accept用来接受端口发来的数据,connfd文件描述符
            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
            continue;
        }
        int n = recv(connfd, buff, MAXLINE, 0);	//将接收到的数据放入buff中
        buff[n] = '\0';
        printf("recv msg from client: %s\n", buff);
        close(connfd);
    }
}

void Server::start()
{
    
    
    Socket();
    Bind();
    Listen();
}

——————————分———————————————割———————————————线————————
client.cc

#include "client.h"

void Client::Error(const char* message)
{
    
    
    printf(message,strerror(errno),errno);
    exit(0);
}

void Client::Socket()
{
    
    
     if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) //初始化套接字,AF_INET表示协议族,SOCK_STREAM是采用TCP协议
        Error("create socket error: %s(errno: %d)\n");// 返回值小于0,报错

    memset(&servaddr, 0, sizeof(servaddr));	//将servaddr结构体的内容置0
    servaddr.sin_family = AF_INET;	//规定协议族为SOCK_STREAM
    servaddr.sin_port = htons(port);//绑定端口
}

void Client::Connet()
{
    
    
    printf("ip:%s\n",ip);
     if( inet_pton(AF_INET, ip, &servaddr.sin_addr) <= 0) //ip转化函数,将ip转化后赋值给servaddr.sin_addr
    {
    
    
        printf("inet_pton error for %s\n",ip);
        exit(0);
    }
    printf("port:%d\n",port);
     if(connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)	//用connet函数进行连接
        Error("connect error: %s(errno: %d)\n");
}

void Client::handle()
{
    
    
    printf("send msg to server: \n");
    fgets(sendline, MAXLINE, stdin);//从屏幕输入
    if( send(sockfd, sendline, strlen(sendline), 0) < 0)	//使用send发送
        Error("send msg error: %s(errno: %d)\n");
}

void Client::start()
{
    
    
    Socket();
    Connet();
}

0X40 Demo

类设计好了,我们现在用一个Demo来测试一下:

Server.cc

#include "server.h"

int main(int argc, char** argv)
{
    
    
    Server* ser=new Server();
    ser->start();
    ser->Handle();
    ser->stop();
return 0;
}

Client.cc

#include "client.h"

int main(int argc, char** argv)
{
    
    
    if( argc != 2){
    
    
        printf("usage: ./client <ipaddress>\n");
        return 0;
    }
    Client* cli= new Client(argv[1]);
    cli->start();
    cli->handle();
    cli->stop();

return 0;
}

makefile

all:server client clean

server:Server.o server.o
	g++ -g -o server Server.o server.o
client:Client.o client.o
	g++ -g -o client Client.o client.o
Server.o:Server.cc
	g++ -g -c Server.cc
Client.o:Client.cc
	g++ -g -c Client.cc
server.o:server.cc
	g++ -g -c server.cc
client.o:client.cc
	g++ -g -c client.cc

clean:
	rm  server.o
	rm Server.o
	rm client.o
	rm Client.o

使用:
make一下
先使用./server来运行服务器端(可以放在本地,也可以放在服务器)
在使用./![client](https://img-blog.csdnimg.cn/20200310231537335.png) <ip address>运行客户端,其中后面的是ip地址,本地请填写127.0.0.1

运行client之后会提示你输入,我们输入Hello World
在这里插入图片描述
之后就会在服务器端显示:
在这里插入图片描述

0X50 后言

好啦,这个最基本的面向对象的socket通信就完成啦

工程放 github 啦,希望大家多多支持~

C++ socket通信编程

猜你喜欢

转载自blog.csdn.net/rjszz1314/article/details/104784235