首页 > 代码库 > Socket编程实践(18) --Socket API 封装(1)

Socket编程实践(18) --Socket API 封装(1)

序言:

    定义一套用于TCP通信比较实用/好用Socket类库(运用C++封装的思想,将socket API尽量封装的好用,实用);

    思想来源:http://www.cnblogs.com/-Lei/archive/2012/09/04/2670942.html

 

Socket.h


#ifndef SOCKET_H_INCLUDED
#define SOCKET_H_INCLUDED

#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <string.h>
#include <fcntl.h>

class Socket
{
public:
    Socket();
    virtual ~Socket();  //virtual destructior

    bool Create(); //Create a socket, server & client can use

    /**Server**/
    bool Bind(int port) const;  //Bind a port
    bool Listen(int backlog = SOMAXCONN) const; //Start to listen
    bool Accept(Socket& clientSocket) const;    //Accept a connect

    /**Client**/
    bool Connect(const std::string& host, int port);

    /** Data Transmission:
        return=0: write or read nothing (if receive == 0,peer connect closed)
        return<0: write error(errno is set)
        return>0: write success(return value is the bytes have send/receive)
    **/
    size_t Send(const Socket& socket, const std::string& message) const;
    size_t Receive(const Socket& socket, std::string& message) const;

    /***All can Use!***/

    //flag: true=SetNonBlock, false=SetBlock
    bool SetNonBlocking(bool flag);

    //return: true -> SetSuccess
    bool SetReuseAddr();

    //test for m_sockfd;
    bool IsValid() const;

private:
    //use m_sockfd to record the result of function socket
    int m_sockfd;
    struct sockaddr_in m_address;
    //define the max bytes to send/write
    static const int BUFSIZ = 1024*10;
};

#endif // SOCKET_H_INCLUDED

Socket.cpp

#include "Socket.h"

Socket::Socket():m_sockfd(-1)
{
}
Socket::~Socket()
{
    if (IsValid())
    {
        close(m_sockfd);
    }
}

bool Socket::Create()
{
    m_sockfd = socket(AF_INET,SOCK_STREAM,0);
    if (IsValid())
        return true;
    return false;
}

//bind a server port at the server
bool Socket::Bind(int port) const
{
    if (!IsValid())
    {
        return false;
    }

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(port);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(m_sockfd,(const struct sockaddr *)&serverAddr,
             sizeof(struct sockaddr)) == -1)
    {
        return false;
    }
    return true;
}

bool Socket::Listen(int backlog) const
{
    if (!IsValid())
    {
        return false;
    }

    if (listen(m_sockfd,backlog) == -1)
    {
        return false;
    }
    return true;
}

bool Socket::Accept(Socket &clientSocket) const
{
    if (!IsValid())
    {
        return false;
    }

    socklen_t clientAddrLength = sizeof(clientSocket.m_address);
    clientSocket.m_sockfd = accept(m_sockfd, (struct sockaddr *)(&clientSocket.m_address),
                                   &clientAddrLength);
    if (clientSocket.m_sockfd == -1)
    {
        return false;
    }
    return true;
}

//Client
bool Socket::Connect(const std::string &serverHost, int serverPort)
{
    if (!IsValid())
    {
        return false;
    }

    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(serverHost.c_str());
    serverAddr.sin_port = htons(serverPort);

    if (connect(m_sockfd,(const struct sockaddr *)(&serverAddr),
                sizeof(struct sockaddr)) == -1)
    {
        return false;
    }

    return true;
}

size_t Socket::Send(const Socket &peerSocket, const std::string &messages) const
{

    struct TransStruct
    {
        size_t bufLen;      //保存真实的报文内容长度
        char buf[BUFSIZ];   //真正要发送的报文内容
    } sendStruct;

    sendStruct.bufLen = messages.length();
    strncpy(sendStruct.buf,messages.c_str(),sendStruct.bufLen);

    ssize_t nWrited = write(peerSocket.m_sockfd,&sendStruct,
                            sendStruct.bufLen+sizeof(size_t));

    if (nWrited == 0)
    {
        return 0;
    }
    else if (nWrited == -1)
    {
        return -1;
    }

    return nWrited;
}

size_t Socket::Receive(const Socket &peerSocket, std::string &messages) const
{
    size_t messageLen = 0;
    if (read(peerSocket.m_sockfd,&messageLen,sizeof(size_t)) == -1)
    {
        return false;
    }

    char buf[BUFSIZ];
    memset(buf,0,sizeof(buf));
    messages.clear();

    ssize_t nRead = read(peerSocket.m_sockfd,buf,messageLen);
    if (nRead == 0)
    {
        return 0;
    }
    else if (nRead == -1)
    {
        return -1;
    }

    messages = buf;
    return nRead;
}

//Set the sockfd NonBlocked
bool Socket::SetNonBlocking(bool flag)
{
    if (IsValid())
    {
        int opts = fcntl(m_sockfd,F_GETFL);
        if (opts == -1)
        {
            return false;
        }

        if (flag)
        {
            opts |= O_NONBLOCK;
        }
        else
        {
            opts &= ~O_NONBLOCK;
        }

        return fcntl(m_sockfd,F_SETFL,opts);
    }

    return false;
}

//Set the address Reused
bool Socket::SetReuseAddr()
{
    if (IsValid())
    {
        int on = 1;
        if (setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) == -1)
        {
            return false;
        }
        else
        {
            return true;
        }
    }
    return false;
}

bool Socket::IsValid() const
{
    //If m_sockfd is not Call Create, m_sockfd == -1
    //Else m_sockfd != -1
    return m_sockfd != -1;
}

测试:一个简陋的echo回声server/client

//server端
#include "comment.h"

int main()
{
    Socket serverSocket;
    if (!serverSocket.Create())
    {
        err_exit("create error");
    }
    if (!serverSocket.Bind(9001))
    {
        err_exit("bind error");
    }
    if (!serverSocket.Listen())
    {
        err_exit("listen error");
    }

    Socket clientSocket;
    if (!serverSocket.Accept(clientSocket))
    {
        err_exit("accept error");
    }

    string message;
    while (true)
    {
        int recvCount = serverSocket.Receive(clientSocket,message);
        if (recvCount == -1)
        {
            err_exit("receive error");
        }
        else if (recvCount == 0)
        {
            cerr << "client connect closed!" << endl;
            exit(0);
        }

        cout << message << endl;
        if (!serverSocket.Send(clientSocket,message))
        {
            err_exit("send error");
        }
    }

    return 0;
}

//client端
#include "comment.h"

int main()
{
    Socket clientSocket;
    if (!clientSocket.Create())
    {
        err_exit("create error");
    }
    if (!clientSocket.Connect("127.0.0.1",9001))
    {
        err_exit("connect error");
    }

    string sendBuf;
    while (getline(cin,sendBuf))
    {
        if (!clientSocket.Send(clientSocket,sendBuf))
        {
            err_exit("send error");
        }
        sendBuf.clear();
        if (!clientSocket.Receive(clientSocket,sendBuf))
        {
            err_exit("receive error");
        }
        cout << sendBuf << endl;
    }

    return 0;
}


-commen.h

#ifndef COMMENT_H_INCLUDED
#define COMMENT_H_INCLUDED

#include "Socket.h"

#include <errno.h>

#include <stdio.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

inline void err_exit(std::string args)
{
    perror(args.c_str());
    _exit(EXIT_FAILURE);
}

#endif // COMMENT_H_INCLUDED

-Makefile

CC = g++ 
CPPFLAGS = -Wall -g -pthread

BIN = server client
SOURCES = $(BIN.=.cpp)

.PHONY: clean all 

all: $(BIN)
$(BIN): $(SOURCES) Socket.cpp

clean:
    -rm -rf $(BIN) bin/ obj/ core

Socket编程实践(18) --Socket API 封装(1)