Socket编程实践(19) --Socket API封装(2)

注:这一片博客与下一篇博客<Socket编程实践(20)>合为一篇,由于代码较多,所以分为两篇,本篇为上篇,主要讲解前一篇讲解的Socket类的增强,下一篇主要讲解怎样使用这个增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)!

思想来源:

1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html

2)http://blog.csdn.net/column/details/linux66.html

3)http://blog.csdn.net/column/details/zjf666.html

修改内容:

1)修改了Socket类几个成员函数的访问权限(由public改为protected, 使Socket类可以进行继承,但不允许私自使用)

2)添加了Close, getAddr, getPort成员函数,使得使用更方便(在下一节的server端代码中可以看到)

3)Send/Receive函数该用著名的writen/readn实现(以前使用的是read/write系统调用),加强了Socket类的容错性.

4)添加了异常处理,让我们在编写易出错的代码时,可以解放思想,不用一直考虑该函数调用出错会发生什么情况!

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

//Protect The Function, Only Used to Server Or Client
protected:
    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;

public:
    /***All can Use!***/

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

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

    //test for m_sockfd;
    bool IsValid() const;
    //close the socket
    bool Close();

public:
    std::string getAddr();
    int getPort() 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"

static ssize_t readn(int fd, void *buf, size_t count);
static ssize_t writen(int fd, const void *buf, size_t count);

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

bool Socket::Close()
{
    if (IsValid())
    {
        ::close(m_sockfd);
        m_sockfd = -1;
        return true;
    }
    return false;
}

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 = writen(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 (readn(peerSocket.m_sockfd,&messageLen,sizeof(size_t)) == -1)
    {
        return false;
    }

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

    ssize_t nRead = readn(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;
}

std::string Socket::getAddr()
{
    if (IsValid())
    {
        return inet_ntoa(m_address.sin_addr);
    }
    return std::string();
}
int Socket::getPort() const
{
    if (IsValid())
    {
        return ntohs(m_address.sin_port);
    }
    return -1;
}

static ssize_t readn(int fd, void *buf, size_t count)
{
    size_t nLeft = count;
    ssize_t nRead = 0;

    char *ptr = static_cast<char *>(buf);

    while (nLeft > 0)
    {
        if ((nRead = read(fd,ptr,nLeft)) < 0)
        {
            //nothing have read!!!
            if (nLeft == count)
            {
                return -1;  //error
            }
            else
            {
                break;  //error, return amount read so far
            }
        }
        else if (nRead == 0)
        {
            break;  //EOF
        }

        //continue to read
        nLeft -= nRead;
        ptr += nRead;
    }

    return count - nLeft;
}
static ssize_t writen(int fd, const void *buf, size_t count)
{
    size_t nLeft = count;
    ssize_t nWritten;

    const char *ptr = static_cast<const char *>(buf);

    while (nLeft > 0)
    {
        if ((nWritten = write(fd,ptr,nLeft)) < 0)
        {
            //nothing have write
            if (nLeft == count)
            {
                return -1;  //error
            }
            else
            {
                break;  //error, return amount write so far
            }
        }
        else if (nWritten == 0)
        {
            break;  //EOF
        }

        nLeft -= nWritten;
        ptr += nWritten;
    }

    return count - nWritten;
}

SocketException.h

#ifndef SOCKETEXCEPTION_H_INCLUDED
#define SOCKETEXCEPTION_H_INCLUDED

//异常处理类(下一节会用到...)
class SocketException
{
public:
    SocketException(const std::string &description):
        m_description(description){}
    ~SocketException(){}

    std::string Description()
    {
        return m_description;
    }

private:
    std::string m_description;
};

#endif // SOCKETEXCEPTION_H_INCLUDED
时间: 2024-08-26 21:46:00

Socket编程实践(19) --Socket API封装(2)的相关文章

Socket编程实践(20) --Socket API封装(3)

注:这一片博客与上一篇博客<Socket编程实践(19)>合为一篇,由于代码较多,所以分为两篇,本篇为下篇,这一篇主要讲解怎样使用上一篇开发的增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.html 3)http://blo

Socket编程实践(3) --Socket API

socket函数 #include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol); 创建一个套接字用于通信 參数: domain:指定通信协议族(protocol family),经常使用取值AF_INET(IPv4) type:指定socket类型, 流式套接字SOCK_STREAM.数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW protoc

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> #inc

Socket编程实践(2) --Socket编程导引

什么是Socket? Socket能够看成是用户进程与内核网络协议栈的接口(编程接口, 例如以下图所看到的), 其不仅能够用于本机进程间通信.能够用于网络上不同主机的进程间通信, 甚至还能够用于异构系统之间的通信. IPv4套接口地址结构 IPv4套接口地址结构通常也称为"网际套接字地址结构".它以"sockaddr_in"命名,定义在头文件<netinet/in.h>中 //TCP/IP地址结构 struct sockaddr_in { uint8_t

Socket编程实践(6) --TCPNotes服务器

僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 假设多个客户端同一时候关闭, 问题描写叙述如以下两幅图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY

Socket编程实践(6) --TCP服务端注意事项

僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 如果多个客户端同时关闭, 问题描述如下面两幅图所示: /** client端实现的测试代码**/ int main() { int s

Socket编程实践(10) --select的限制与poll的使用

select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或者使用setrlimit函数设置,但一个系统所能打开的最大数也是有限的,跟内存大小有关,可以通过cat /proc/sys/fs/file-max 查看 /**示例: getrlimit/setrlimit获取/设置进程打开文件数目**/ int main() { struct rlimit rl;

socket编程之三:socket网络编程中的常用函数

这节本来打算先给出常用函数介绍,再给两个代码实例,写着写着发现越来越长,决定把代码放在下一节. 本节内容持续更新...... 1 socket()函数 原型: int socket(int domain, int type, int protocol); 描述: 类似打开一个文件,返回一个socket描述符,唯一标识一个socket,后面相应的操作都是这用这个socket描述符. 参数: domain:协议族,常用的协议族有AF_INET.AF_INET6.AF_LOCAL.AF_ROUTE等:

Socket编程实践(3)

Socket 基础API实践(2)--实现回射服务器 accept函数 功能:从已完成连接队列返回第一个连接,如果已完成连接队列为空,则阻塞. 原型 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 参数 sockfd:服务器套接字 addr:将返回对等方的套接字地址 addrlen:返回对等方的套接字地址长度 返回值 On  success, these system calls return a nonneg