muduo源码解析21-Socket类

Socket类:

说明:

封装了一个套接字Sokcet类,内部只保存一个套接字文件描述符m_sockfd
提供了常用的套接字相关的操作,例如:
绑定套接字,监听,接受一个连接,关闭写操作,获取Tcp信息,
设置套接字为NO_DELAY模式,设置地址重用,端口重用,设置keepalive

成员变量:

private:
    //套接字文件描述符,用const修饰,只能在构造函数初始化列表中初始化,不允许作为左值
    const int m_sockfd;

成员函数:

public:
    //explicit禁止隐式转换
    explicit Socket(int sockfd)
        :m_sockfd(sockfd){}
    ~Socket();

    //获取m_sockfd
    int fd() const{return m_sockfd;}

    //获取TCP信息,利用getsockopt()实现
    bool getTcpInfo(struct tcp_info*) const;
    //获取TCP信息,保存到字符串buf中
    bool getTcpInfoString(char* buf,int len) const;

    //绑定
    void bindAddress(const inetaddress& localaddr);
    //监听
    void listen();
    //接受一个连接
    int accept(inetaddress* peeraddr);

    //关闭写操作
    void shutdownWrite();

    //设置TCP no delay,下面四个都是通过setsockopt()函数设置套接字模式
    void setTcpNoDelay(bool on);

    //地址与端口重用
    void setReuseAddr(bool on);
    void setReusePort(bool on);

    //设置keepalive
    void setKeepAlive(bool on);

Socket.h:

#ifndef SOCKET_H
#define SOCKET_H

#include"base/noncopyable.h"

//struct tcp_info is in <netinet/tcp.h>
struct tcp_info;

namespace mymuduo {

namespace net{

class inetaddress;

class Socket
{
public:
    //explicit禁止隐式转换
    explicit Socket(int sockfd)
        :m_sockfd(sockfd){}
    ~Socket();

    //获取m_sockfd
    int fd() const{return m_sockfd;}

    //获取TCP信息,利用getsockopt()实现
    bool getTcpInfo(struct tcp_info*) const;
    //获取TCP信息,保存到字符串buf中
    bool getTcpInfoString(char* buf,int len) const;

    //绑定
    void bindAddress(const inetaddress& localaddr);
    //监听
    void listen();
    //接受一个连接
    int accept(inetaddress* peeraddr);

    //关闭写操作
    void shutdownWrite();

    //设置TCP no delay,下面四个都是通过setsockopt()函数设置套接字模式
    void setTcpNoDelay(bool on);

    //地址与端口重用
    void setReuseAddr(bool on);
    void setReusePort(bool on);

    //设置keepalive
    void setKeepAlive(bool on);

private:
    //套接字文件描述符,用const修饰,只能在构造函数初始化列表中初始化,不允许作为左值
    const int m_sockfd;

};

}//namespace net

}//namespace mymuduo



#endif // SOCKET_H

 

Socket.cpp

#include "socket.h"
#include"base/logging.h"
#include"net/inetaddress.h"
#include"net/socketsops.h"

#include<netinet/in.h>
#include<netinet/tcp.h>
#include<stdio.h>

namespace mymuduo{

namespace net {

//析构时关闭套接字
Socket::~Socket()
{
    sockets::close(m_sockfd);
}

//获取TCP信息,内部使用getsockopt实现,保存到tcp_info*中
bool Socket::getTcpInfo(struct tcp_info* tcpi) const
{
    socklen_t len=static_cast<socklen_t>(sizeof(*tcpi));
    memZero(tcpi,len);
    return ::getsockopt(m_sockfd,SOL_TCP,TCP_INFO,tcpi,&len);
}

//同样是获取TCP信息,把TCP信息保存到buf中
bool Socket::getTcpInfoString(char* buf,int len) const
{
    struct tcp_info tcpi;
    bool ok=getTcpInfo(&tcpi);
    if(ok)
    {
        snprintf(buf, len, "unrecovered=%u "
                           "rto=%u ato=%u snd_mss=%u rcv_mss=%u "
                           "lost=%u retrans=%u rtt=%u rttvar=%u "
                           "sshthresh=%u cwnd=%u total_retrans=%u",
                 tcpi.tcpi_retransmits,  // Number of unrecovered [RTO] timeouts
                 tcpi.tcpi_rto,          // Retransmit timeout in usec
                 tcpi.tcpi_ato,          // Predicted tick of soft clock in usec
                 tcpi.tcpi_snd_mss,
                 tcpi.tcpi_rcv_mss,
                 tcpi.tcpi_lost,         // Lost packets
                 tcpi.tcpi_retrans,      // Retransmitted packets out
                 tcpi.tcpi_rtt,          // Smoothed round trip time in usec
                 tcpi.tcpi_rttvar,       // Medium deviation
                 tcpi.tcpi_snd_ssthresh,
                 tcpi.tcpi_snd_cwnd,
                 tcpi.tcpi_total_retrans);  // Total retransmits for entire connection
    }
    return ok;
}

//绑定m_sockfd到inetaddress& addr上
void Socket::bindAddress(const inetaddress& addr)
{
    sockets::bindOrDie(m_sockfd,addr.getSockAddr());
}

//在m_sockfd上监听操作
void Socket::listen()
{
    sockets::listenOrDie(m_sockfd);
}

//关闭m_sockfd写操作
void Socket::shutdownWrite()
{
    sockets::shutdownWrite(m_sockfd);
}

//在m_sockfd上接受一个连接,如果成功把客户机sockaddr保存到inetaddress* peeraddr中
int Socket::accept(inetaddress* peeraddr)
{
    struct sockaddr_in6 addr;
    memZero(&addr,sizeof(addr));
    int connfd=sockets::accept(m_sockfd,&addr);
    if(connfd>=0)
        peeraddr->setSockAddrInet6(addr);
    return connfd;
}

//禁用Nigle算法,设置NO DELAY
void Socket::setTcpNoDelay(bool on)
{
    int optval=on?1:0;
    ::setsockopt(m_sockfd,IPPROTO_TCP,TCP_NODELAY,&optval,
                 static_cast<socklen_t>(sizeof(optval)));
}

//设置IP地址可重用
void Socket::setReuseAddr(bool on)
{
    int optval=on?1:0;
    ::setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,
                 static_cast<socklen_t>(sizeof(optval)));
}

//设置端口可重用
void Socket::setReusePort(bool on)
{
#ifdef SO_REUSEPORT
    int optval=on?1:0;
    int ret=::setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,
                         static_cast<socklen_t>(sizeof(optval)));
    if(ret<0 && on)
        LOG_SYSERR<<"SO_REUSEPORT failed";
#else
    if(on)
    {
        LOG_ERROR << "SO_REUSEPORT is not supported.";
    }
#endif
}

//设置是否开启keepalive判断通信方是否存活
void Socket::setKeepAlive(bool on)
{
    int optval=on?1:0;
    ::setsockopt(m_sockfd,SOL_SOCKET,SO_KEEPALIVE,&optval,
                 static_cast<socklen_t>(sizeof(optval)));

}

}//namespace net

}//namespace mymuduo

测试:

还是创建两个线程模拟客户端和服务端。

#include "base/logging.h"
#include"base/thread.h"
#include"net/socket.h"
#include"net/inetaddress.h"
#include"net/socketsops.h"

#include<fcntl.h>

#include<iostream>

using namespace mymuduo::net;

//现在不用直接调用socketsops.h中的函数了,直接利用Socket和inetaddress这两个类进行操作就可以了
//为了测试能否正确连接与收发数据,暂时用fcntl把server和client都设置成阻塞模式

//步骤: socket,bing,listen,accpet,read,print TCP info
void serverThread()
{
    Socket server(sockets::createNonblockingOrDie(AF_INET));
    //为了测试,还是设置成非阻塞模式把
    int cfd=server.fd();
    fcntl(cfd,F_SETFL,fcntl(cfd,F_GETFL,0)&~O_NONBLOCK);

    inetaddress serveraddr("192.168.1.103",12306);
    server.bindAddress(serveraddr);
    server.listen();
    inetaddress clientaddr;
    int client=server.accept(&clientaddr);

    //现在还没有实现read/write的封装,利用sockets::read/write()来实现
    if(client>0)
        std::cout<<clientaddr.toIpPort()<<" connected in...\n";

    char buf[1024]={0};
    sockets::read(client,buf,1024);

    std::cout<<"recv: "<<buf<<std::endl;

    memset(&buf,0,1024);
    server.getTcpInfoString(buf,1024);
    std::cout<<"server TCP info: \n"<<buf<<std::endl;

}

//步骤:socket,connect,write,print TCP info
void clientThread()
{
    Socket client(sockets::createNonblockingOrDie(AF_INET));
    //为了测试,还是设置成非阻塞模式把
    int cfd=client.fd();
    fcntl(cfd,F_SETFL,fcntl(cfd,F_GETFL,0)&~O_NONBLOCK);

    inetaddress serveraddr("192.168.1.103",12306);

    sockets::connect(client.fd(),serveraddr.getSockAddr());

    sockets::write(client.fd(),"nmsl",4);

    char buf[1024]={0};
    client.getTcpInfoString(buf,1024);
    std::cout<<"client TCP info: \n"<<buf<<std::endl;
}

int main()
{

    mymuduo::thread ts(serverThread,"serverThread"),
            tc(clientThread,"clientThread");
    ts.start();
    tc.start();

    ts.join();
    tc.join();

}

打印信息:

192.168.1.103:35504 connected in...
client TCP info:

recv: nmsl
server TCP info:

可以看到,能够建立连接和收发数据,但是Socket::getTcpInfo(struct tcp_info*) const调用失败

也就是getsockopt(m_sockfd,SOL_TCP,TCP_INFO,tcpi,&len)调用失败,

没法获取tcp_info数据,有点没搞懂。

 

posted @ 2020-08-28 21:22  WoodInEast  阅读(216)  评论(0编辑  收藏  举报