封装了一个套接字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);
#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
#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数据,有点没搞懂。
原文:https://www.cnblogs.com/woodineast/p/13579907.html