net6. 套接字封装类——Endian、SocketsOps、Socket、InetAddress

Endian.h
封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)。

SocketsOps.h/ SocketsOps.cc
封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)。

Socket.h/Socket.cc(Socket类)

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

InetAddress.h/InetAddress.cc(InetAddress类)
网际地址sockaddr_in封装。

1.Endian类

成员函数:

inline uint64_t hostToNetwork64(uint64_t host64):主机字节序转换为网络字节序,对64位整数进行转换
inline uint32_t hostToNetwork32(uint32_t host32):主机字节序转换为网络字节序,对32位整数进行转换
inline uint16_t hostToNetwork16(uint16_t host16):主机字节序转换为网络字节序,对16位整数进行转换
inline uint64_t networkToHost64(uint64_t net64):网络字节序转换为主机字节序,对64位整数进行转换
inline uint32_t networkToHost32(uint32_t net32):网络字节序转换为主机字节序,对32位整数进行转换
inline uint16_t networkToHost16(uint16_t net16):网络字节序转换为主机字节序,对16位整数进行转换

Endian.h

//封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)
#ifndef MUDUO_NET_ENDIAN_H
#define MUDUO_NET_ENDIAN_H

#include <stdint.h>
#include <endian.h>

namespace muduo
{
namespace net
{
namespace sockets
{

// the inline assembler code makes type blur,
// so we disable warnings for a while.
#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic push
#endif
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wold-style-cast"

//主机字节序转换为网络字节序,对64位整数进行转换
inline uint64_t hostToNetwork64(uint64_t host64)
{
  return htobe64(host64);
}

//主机字节序转换为网络字节序,对32位整数进行转换
inline uint32_t hostToNetwork32(uint32_t host32)
{
  return htobe32(host32);
}

//主机字节序转换为网络字节序,对16位整数进行转换
inline uint16_t hostToNetwork16(uint16_t host16)
{
  return htobe16(host16);
}

//网络字节序转换为主机字节序,对64位整数进行转换
inline uint64_t networkToHost64(uint64_t net64)
{
  return be64toh(net64);
}

//网络字节序转换为主机字节序,对32位整数进行转换
inline uint32_t networkToHost32(uint32_t net32)
{
  return be32toh(net32);
}

//网络字节序转换为主机字节序,对16位整数进行转换
inline uint16_t networkToHost16(uint16_t net16)
{
  return be16toh(net16);
}
#if __GNUC_MINOR__ >= 6
#pragma GCC diagnostic pop
#else
#pragma GCC diagnostic error "-Wconversion"
#pragma GCC diagnostic error "-Wold-style-cast"
#endif


}
}
}

#endif  // MUDUO_NET_ENDIAN_H

2.SocketsOps类

成员函数:

int createNonblockingOrDie():创建一个非阻塞的套接字,如果创建失败,则终止程序
int connect(int sockfd, const struct sockaddr_in& addr):连接connect函数
void bindOrDie(int sockfd, const struct sockaddr_in& addr):绑定bind,如果绑定失败,则终止程序
void listenOrDie(int sockfd):监听listen,如果监听失败,则终止程序
int accept(int sockfd, struct sockaddr_in* addr):接受连接accept函数
ssize_t read(int sockfd, void buf, size_t count):从sockfd中读数据到buf
ssize_t readv(int sockfd, const struct iovec iov, int iovcnt):readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
ssize_t write(int sockfd, const void buf, size_t count):将buf中的数据写入到sockfd
void close(int sockfd):关闭文件描述符
void shutdownWrite(int sockfd):关闭连接写的这一半
void toIpPort(char
buf, size_t size, const struct sockaddr_in& addr):toIpPort()将地址addr转换为IP与端口的形式
void toIp(char
buf, size_t size, const struct sockaddr_in& addr):toIp()被toIpPort()调用,将地址addr转换为点分十进制的IP形式
void fromIpPort(const char
ip, uint16_t port, struct sockaddr_in* addr):fromIpPort()从IP和端口构造一个地址addr
int getSocketError(int sockfd):返回sockfd的错误
struct sockaddr_in getLocalAddr(int sockfd):获取sockfd套接字的本地地址
struct sockaddr_in getPeerAddr(int sockfd):获取sockfd套接字的对等方地址
bool isSelfConnect(int sockfd):判断是否是自连接

SocketsOps.h

// This is an internal header file, you should not include this.
//封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)
#ifndef MUDUO_NET_SOCKETSOPS_H
#define MUDUO_NET_SOCKETSOPS_H

#include <arpa/inet.h>

namespace muduo
{
namespace net
{
namespace sockets
{

///
/// Creates a non-blocking socket file descriptor,
/// abort if any error.
//创建一个非阻塞的套接字,如果创建失败,则终止程序
int createNonblockingOrDie();

//连接connect函数
int  connect(int sockfd, const struct sockaddr_in& addr);
//绑定bind,如果绑定失败,则终止程序
void bindOrDie(int sockfd, const struct sockaddr_in& addr);
//监听listen,如果监听失败,则终止程序
void listenOrDie(int sockfd);
//接受连接accept函数
int  accept(int sockfd, struct sockaddr_in* addr);
//从sockfd中读数据到buf
ssize_t read(int sockfd, void *buf, size_t count);
//readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
ssize_t readv(int sockfd, const struct iovec *iov, int iovcnt);
//将buf中的数据写入到sockfd
ssize_t write(int sockfd, const void *buf, size_t count);
//关闭文件描述符
void close(int sockfd);
//关闭连接写的这一半
void shutdownWrite(int sockfd);

//toIpPort()将地址addr转换为IP与端口的形式
void toIpPort(char* buf, size_t size,
              const struct sockaddr_in& addr);
//toIp()被toIpPort()调用,将地址addr转换为点分十进制的IP形式
void toIp(char* buf, size_t size,
          const struct sockaddr_in& addr);
//fromIpPort()从IP和端口构造一个地址addr
void fromIpPort(const char* ip, uint16_t port,
                  struct sockaddr_in* addr);

//返回sockfd的错误
int getSocketError(int sockfd);

//获取sockfd套接字的本地地址
struct sockaddr_in getLocalAddr(int sockfd);
//获取sockfd套接字的对等方地址
struct sockaddr_in getPeerAddr(int sockfd);
//判断是否是自连接
bool isSelfConnect(int sockfd);

}
}
}

#endif  // MUDUO_NET_SOCKETSOPS_H

SocketsOps.cc

#include <muduo/net/SocketsOps.h>

#include <muduo/base/Logging.h>
#include <muduo/base/Types.h>
#include <muduo/net/Endian.h>

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>  // snprintf
#include <strings.h>  // bzero
#include <sys/socket.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

namespace
{

typedef struct sockaddr SA;

//将网际地址指针转换为通用地址指针,const版本
const SA* sockaddr_cast(const struct sockaddr_in* addr)
{
  //implicit_cast()定义在muduo/base/Types.h中
  return static_cast<const SA*>(implicit_cast<const void*>(addr));
}

//将网际地址指针转换为通用地址指针,无const版本
SA* sockaddr_cast(struct sockaddr_in* addr)
{
  return static_cast<SA*>(implicit_cast<void*>(addr));
}

//将文件描述符sockfd设置为非阻塞模式
void setNonBlockAndCloseOnExec(int sockfd)
{
  // non-block
  int flags = ::fcntl(sockfd, F_GETFL, 0);
  flags |= O_NONBLOCK;
  int ret = ::fcntl(sockfd, F_SETFL, flags);
  // FIXME check

  // close-on-exec
  flags = ::fcntl(sockfd, F_GETFD, 0);
  flags |= FD_CLOEXEC;
  ret = ::fcntl(sockfd, F_SETFD, flags);
  // FIXME check

  (void)ret;
}

}

int sockets::createNonblockingOrDie()
{
  // socket
  // VALGRIND可以检测内存泄漏和文件描述符的打开状态
#if VALGRIND
  int sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
  }

  setNonBlockAndCloseOnExec(sockfd);
#else
  // Linux 2.6.27以上的内核支持SOCK_NONBLOCK与SOCK_CLOEXEC
  int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "sockets::createNonblockingOrDie";
  }
#endif
  return sockfd;
}

void sockets::bindOrDie(int sockfd, const struct sockaddr_in& addr)
{
  int ret = ::bind(sockfd, sockaddr_cast(&addr), sizeof addr);
  if (ret < 0)
  {
    LOG_SYSFATAL << "sockets::bindOrDie";
  }
}

void sockets::listenOrDie(int sockfd)
{
  int ret = ::listen(sockfd, SOMAXCONN);
  if (ret < 0)
  {
    LOG_SYSFATAL << "sockets::listenOrDie";
  }
}

int sockets::accept(int sockfd, struct sockaddr_in* addr)
{
  socklen_t addrlen = sizeof *addr;
#if VALGRIND
  int connfd = ::accept(sockfd, sockaddr_cast(addr), &addrlen);
  setNonBlockAndCloseOnExec(connfd);
#else
  int connfd = ::accept4(sockfd, sockaddr_cast(addr),
                         &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
#endif
  if (connfd < 0)
  {
    int savedErrno = errno;
    LOG_SYSERR << "Socket::accept";
    switch (savedErrno)
    {
      case EAGAIN:
      case ECONNABORTED:
      case EINTR:
      case EPROTO: // ???
      case EPERM:
      case EMFILE: // per-process lmit of open file desctiptor ???
        // expected errors
        errno = savedErrno;
        break;
      case EBADF:
      case EFAULT:
      case EINVAL:
      case ENFILE:
      case ENOBUFS:
      case ENOMEM:
      case ENOTSOCK:
      case EOPNOTSUPP:
        // unexpected errors
        LOG_FATAL << "unexpected error of ::accept " << savedErrno;
        break;
      default:
        LOG_FATAL << "unknown error of ::accept " << savedErrno;
        break;
    }
  }
  return connfd;
}

int sockets::connect(int sockfd, const struct sockaddr_in& addr)
{
  return ::connect(sockfd, sockaddr_cast(&addr), sizeof addr);
}

ssize_t sockets::read(int sockfd, void *buf, size_t count)
{
  return ::read(sockfd, buf, count);
}

// readv与read不同之处在于,接收的数据可以填充到多个缓冲区中
ssize_t sockets::readv(int sockfd, const struct iovec *iov, int iovcnt)
{
  return ::readv(sockfd, iov, iovcnt);
}

ssize_t sockets::write(int sockfd, const void *buf, size_t count)
{
  return ::write(sockfd, buf, count);
}

void sockets::close(int sockfd)
{
  if (::close(sockfd) < 0)
  {
    LOG_SYSERR << "sockets::close";
  }
}

// 只关闭写的这一半
void sockets::shutdownWrite(int sockfd)
{
  if (::shutdown(sockfd, SHUT_WR) < 0)
  {
    LOG_SYSERR << "sockets::shutdownWrite";
  }
}

void sockets::toIpPort(char* buf, size_t size,
                       const struct sockaddr_in& addr)
{
  char host[INET_ADDRSTRLEN] = "INVALID";
  toIp(host, sizeof host, addr);
  uint16_t port = sockets::networkToHost16(addr.sin_port);
  snprintf(buf, size, "%s:%u", host, port);
}

void sockets::toIp(char* buf, size_t size,
                   const struct sockaddr_in& addr)
{
  assert(size >= INET_ADDRSTRLEN);
  ::inet_ntop(AF_INET, &addr.sin_addr, buf, static_cast<socklen_t>(size));
}

void sockets::fromIpPort(const char* ip, uint16_t port,
                           struct sockaddr_in* addr)
{
  addr->sin_family = AF_INET;
  addr->sin_port = hostToNetwork16(port);
  if (::inet_pton(AF_INET, ip, &addr->sin_addr) <= 0)
  {
    LOG_SYSERR << "sockets::fromIpPort";
  }
}

int sockets::getSocketError(int sockfd)
{
  int optval;
  socklen_t optlen = sizeof optval;

  if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)
  {
    return errno;
  }
  else
  {
    return optval;
  }
}

struct sockaddr_in sockets::getLocalAddr(int sockfd)
{
  struct sockaddr_in localaddr;
  bzero(&localaddr, sizeof localaddr);
  socklen_t addrlen = sizeof(localaddr);
  if (::getsockname(sockfd, sockaddr_cast(&localaddr), &addrlen) < 0)
  {
    LOG_SYSERR << "sockets::getLocalAddr";
  }
  return localaddr;
}

struct sockaddr_in sockets::getPeerAddr(int sockfd)
{
  struct sockaddr_in peeraddr;
  bzero(&peeraddr, sizeof peeraddr);
  socklen_t addrlen = sizeof(peeraddr);
  if (::getpeername(sockfd, sockaddr_cast(&peeraddr), &addrlen) < 0)
  {
    LOG_SYSERR << "sockets::getPeerAddr";
  }
  return peeraddr;
}

// 自连接是指(sourceIP, sourcePort) = (destIP, destPort)
// 自连接发生的原因:
// 客户端在发起connect的时候,没有bind(2)
// 客户端与服务器端在同一台机器,即sourceIP = destIP,
// 服务器尚未开启,即服务器还没有在destPort端口上处于监听
// 就有可能出现自连接,这样,服务器也无法启动了

bool sockets::isSelfConnect(int sockfd)
{
  struct sockaddr_in localaddr = getLocalAddr(sockfd);
  struct sockaddr_in peeraddr = getPeerAddr(sockfd);
  return localaddr.sin_port == peeraddr.sin_port
      && localaddr.sin_addr.s_addr == peeraddr.sin_addr.s_addr;
}


3.Socket类

数据成员:

const int sockfd_:文件描述符sockfd_

成员函数:

explicit Socket(int sockfd):构造函数
~Socket():析构函数
int fd() const:返回文件描述符
void bindAddress(const InetAddress& localaddr):绑定函数
void listen():监听函数
int accept(InetAddress* peeraddr):接受连接函数
void shutdownWrite():关闭连接写的这一半
void setTcpNoDelay(bool on):setTcpNoDelay()函数禁用Nagle算法,可以避免连续发包出现延迟,这对于编写低延迟的网络服务很重要
void setReuseAddr(bool on):设置地址重复利用
void setKeepAlive(bool on):setKeepAlive()是TCP keepalive是指定期探测连接是否存在,如果应用层有心跳的话,这个选项不是必需要设置的

Socket.h

// This is an internal header file, you should not include this.
//用RAII方法封装socket file descriptor
#ifndef MUDUO_NET_SOCKET_H
#define MUDUO_NET_SOCKET_H

#include <boost/noncopyable.hpp>

namespace muduo
{
///
/// TCP networking.
///
namespace net
{

class InetAddress;

///
/// Wrapper of socket file descriptor.
///
/// It closes the sockfd when desctructs.
/// It's thread safe, all operations are delagated to OS.
class Socket : boost::noncopyable
{
 public:
  explicit Socket(int sockfd)
    : sockfd_(sockfd)
  { }

  // Socket(Socket&&) // move constructor in C++11
  //析构函数,在对象的生命期结束时,调用close()函数确保套接字描述符被关闭
  ~Socket();
  
  //返回文件描述符
  int fd() const { return sockfd_; }

  /// abort if address in use
  //绑定函数
  void bindAddress(const InetAddress& localaddr);
  /// abort if address in use
  //监听函数
  void listen();

  /// On success, returns a non-negative integer that is
  /// a descriptor for the accepted socket, which has been
  /// set to non-blocking and close-on-exec. *peeraddr is assigned.
  /// On error, -1 is returned, and *peeraddr is untouched.
  //接受连接函数
  int accept(InetAddress* peeraddr);
  
  //关闭连接写的这一半
  void shutdownWrite();

  ///
  /// Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm).
  ///
  // Nagle算法可以一定程度上避免网络拥塞
  // TCP_NODELAY选项可以禁用Nagle算法
  // setTcpNoDelay()函数禁用Nagle算法,可以避免连续发包出现延迟,这对于编写低延迟的网络服务很重要
  void setTcpNoDelay(bool on);

  ///
  /// Enable/disable SO_REUSEADDR
  ///
  //设置地址重复利用
  void setReuseAddr(bool on);

  ///
  /// Enable/disable SO_KEEPALIVE
  ///
  // setKeepAlive()是TCP keepalive是指定期探测连接是否存在,如果应用层有心跳的话,这个选项不是必需要设置的
  void setKeepAlive(bool on);

 private:
  //文件描述符sockfd_
  const int sockfd_;
};

}
}
#endif  // MUDUO_NET_SOCKET_H

Socket.cc

#include <muduo/net/Socket.h>

#include <muduo/net/InetAddress.h>
#include <muduo/net/SocketsOps.h>

#include <netinet/in.h>
#include <netinet/tcp.h>
#include <strings.h>  // bzero

using namespace muduo;
using namespace muduo::net;

Socket::~Socket()
{
  sockets::close(sockfd_);
}

void Socket::bindAddress(const InetAddress& addr)
{
  sockets::bindOrDie(sockfd_, addr.getSockAddrInet());
}

void Socket::listen()
{
  sockets::listenOrDie(sockfd_);
}

int Socket::accept(InetAddress* peeraddr)
{
  struct sockaddr_in addr;
  bzero(&addr, sizeof addr);
  int connfd = sockets::accept(sockfd_, &addr);
  if (connfd >= 0)
  {
    peeraddr->setSockAddrInet(addr);
  }
  return connfd;
}

void Socket::shutdownWrite()
{
  sockets::shutdownWrite(sockfd_);
}

void Socket::setTcpNoDelay(bool on)
{
  int optval = on ? 1 : 0;
  ::setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY,
               &optval, sizeof optval);
  // FIXME CHECK
}

void Socket::setReuseAddr(bool on)
{
  int optval = on ? 1 : 0;
  ::setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR,
               &optval, sizeof optval);
  // FIXME CHECK
}

void Socket::setKeepAlive(bool on)
{
  int optval = on ? 1 : 0;
  ::setsockopt(sockfd_, SOL_SOCKET, SO_KEEPALIVE,
               &optval, sizeof optval);
  // FIXME CHECK
}


4.InetAddress类

数据成员:

struct sockaddr_in addr_:网际地址addr_

成员函数:

explicit InetAddress(uint16_t port):构造函数,构造一个地址,仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0),一般可用于监听
InetAddress(const StringPiece& ip, uint16_t port):构造函数,给定了IP也给定了端口来构造一个地址
InetAddress(const struct sockaddr_in& addr):构造函数,给定一个地址addr来构造网际地址addr_
string toIp() const:将addr_转换成IP形式
string toIpPort() const:将addr_转换成IP与端口的形式
string toHostPort() const attribute ((deprecated)):这个函数的功能和toIpPort()是一样的,attribute ((deprecated)) 表示该函数是过时的,被淘汰的,这样使用该函数,在编译的时候,会发出警告
const struct sockaddr_in& getSockAddrInet() const:返回网际地址addr_
void setSockAddrInet(const struct sockaddr_in& addr):设置网际地址addr_的值
uint32_t ipNetEndian() const:返回网络字节序的32位整数IP
uint16_t portNetEndian() const:返回网络字节序的端口

InetAddress.h

//网际地址sockaddr_in封装
#ifndef MUDUO_NET_INETADDRESS_H
#define MUDUO_NET_INETADDRESS_H

#include <muduo/base/copyable.h>
#include <muduo/base/StringPiece.h>

#include <netinet/in.h>

namespace muduo
{
namespace net
{

///
/// Wrapper of sockaddr_in.
///
/// This is an POD interface class.
//网际地址sockaddr_in封装
class InetAddress : public muduo::copyable
{
 public:
  /// Constructs an endpoint with given port number.
  /// Mostly used in TcpServer listening.
  // 构造函数,构造一个地址,仅仅指定port,不指定ip,则ip为INADDR_ANY(即0.0.0.0),一般可用于监听
  explicit InetAddress(uint16_t port);

  /// Constructs an endpoint with given ip and port.
  /// @c ip should be "1.2.3.4"
  //构造函数,给定了IP也给定了端口来构造一个地址
  InetAddress(const StringPiece& ip, uint16_t port);

  /// Constructs an endpoint with given struct @c sockaddr_in
  /// Mostly used when accepting new connections
  //构造函数,给定一个地址addr来构造网际地址addr_
  InetAddress(const struct sockaddr_in& addr)
    : addr_(addr)
  { }
  
  //将addr_转换成IP形式
  string toIp() const;
  //将addr_转换成IP与端口的形式
  string toIpPort() const;

  // 这个函数的功能和toIpPort()是一样的,__attribute__ ((deprecated)) 表示该函数是过时的,被淘汰的
  // 这样使用该函数,在编译的时候,会发出警告
  string toHostPort() const __attribute__ ((deprecated))
  { return toIpPort(); }

  // default copy/assignment are Okay
  
  //返回网际地址addr_
  const struct sockaddr_in& getSockAddrInet() const { return addr_; }
  //设置网际地址addr_的值
  void setSockAddrInet(const struct sockaddr_in& addr) { addr_ = addr; }
  
  //返回网络字节序的32位整数IP
  uint32_t ipNetEndian() const { return addr_.sin_addr.s_addr; }
  //返回网络字节序的端口
  uint16_t portNetEndian() const { return addr_.sin_port; }

 private:
  //网际地址addr_
  struct sockaddr_in addr_;
};

}
}

#endif  // MUDUO_NET_INETADDRESS_H

InetAddress.cc

#include <muduo/net/InetAddress.h>

#include <muduo/net/Endian.h>
#include <muduo/net/SocketsOps.h>

#include <strings.h>  // bzero
#include <netinet/in.h>

#include <boost/static_assert.hpp>

// INADDR_ANY use (type)value casting.
#pragma GCC diagnostic ignored "-Wold-style-cast"
static const in_addr_t kInaddrAny = INADDR_ANY;
#pragma GCC diagnostic error "-Wold-style-cast"

//     /* Structure describing an Internet socket address.  */
//     struct sockaddr_in {
//         sa_family_t    sin_family; /* address family: AF_INET */
//         uint16_t       sin_port;   /* port in network byte order */
//         struct in_addr sin_addr;   /* internet address */
//     };

//     /* Internet address. */
//     typedef uint32_t in_addr_t;
//     struct in_addr {
//         in_addr_t       s_addr;     /* address in network byte order */
//     };

using namespace muduo;
using namespace muduo::net;

BOOST_STATIC_ASSERT(sizeof(InetAddress) == sizeof(struct sockaddr_in));

InetAddress::InetAddress(uint16_t port)
{
  bzero(&addr_, sizeof addr_);
  addr_.sin_family = AF_INET;
  addr_.sin_addr.s_addr = sockets::hostToNetwork32(kInaddrAny);
  addr_.sin_port = sockets::hostToNetwork16(port);
}

InetAddress::InetAddress(const StringPiece& ip, uint16_t port)
{
  bzero(&addr_, sizeof addr_);
  sockets::fromIpPort(ip.data(), port, &addr_);
}

string InetAddress::toIpPort() const
{
  char buf[32];
  sockets::toIpPort(buf, sizeof buf, addr_);
  return buf;
}

string InetAddress::toIp() const
{
  char buf[32];
  sockets::toIp(buf, sizeof buf, addr_);
  return buf;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在使用 Python 的 socket 模块创建 socket 时,可以设置 IP 地址。具体方法是通过传入一个元组来指定 IP 地址和端口号。 例如,创建一个 TCP/IP socket 并指定 IP 地址为 '127.0.0.1',端口号为 8080,代码如下: ``` import socket # 创建一个 TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定 IP 地址和端口号 server_address = ('127.0.0.1', 8080) sock.bind(server_address) # 监听端口 sock.listen(1) print('Server started and listening on {}:{}'.format(*server_address)) # 接受连接请求 connection, client_address = sock.accept() # 处理连接 try: while True: data = connection.recv(1024) if data: print('Received data: {!r}'.format(data)) else: break finally: # 关闭连接 connection.close() print('Connection closed') ``` ### 回答2: 在使用Python的socket模块时,可以通过socket.socket来创建一个TCP套接字对象,然后通过该对象的方法设置IP地址。 首先,需要在程序中导入socket模块: ```python import socket ``` 接下来,可以使用`socket.socket()`方法创建一个套接字对象,如: ```python sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ``` 在这里,`AF_INET`表示使用IPv4协议,`SOCK_STREAM`表示使用TCP协议。 要设置IP地址,可以使用`socket.bind()`方法,参数是一个元组,包含IP地址和端口号。例如,将IP地址设置为本地主机的IP地址,并监听特定的端口号,可以这样设置: ```python host = '127.0.0.1' # 设置IP地址为本地主机的IP地址 port = 12345 # 设置端口号为12345 sock.bind((host, port)) # 绑定IP地址和端口号 ``` IP地址是一个由四个十进制数值(0-255)组成的串,用点分隔开,如"127.0.0.1"。在本例中,我们将IP地址设置为本地主机的IP地址,即`'127.0.0.1'`。 最后,我们可以通过`socket.getsockname()`方法来获取套接字的设置的IP地址和端口号。例如: ```python ip, port = sock.getsockname() print("IP地址:", ip) print("端口号:", port) ``` 这样就完成了通过socket.socket设置IP地址的操作。 ### 回答3: 在使用socket编程时,可以使用socket.socket来创建套接字。在创建套接字时,可以通过设置IP地址来指定该套接字要绑定的网络接口。 设置IP地址的方式是通过套接字的bind()方法来实现的。bind()方法接受一个参数,即所要绑定的IP地址。 要设置IP地址,需要将IP地址转换为所需的格式。在IPv4协议中,IP地址通常以字符串的形式表示,例如"192.168.0.1"。而在socket编程中,IP地址需要使用网络字节序(big-endian)表示,可以使用socket库中的inet_aton函数或inet_pton函数将字符串格式的IP地址转换为网络字节序。 以下是一个示例代码,实现了创建一个套接字并设置IP地址为"192.168.0.1"的过程: ```python import socket # 创建套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置IP地址 ip = "192.168.0.1" ip_bytes = socket.inet_aton(ip) # 将IP地址转换为网络字节序 sock.bind((ip_bytes, 0)) # 绑定套接字 # 其他操作... ``` 在上述代码中,首先使用socket.socket()方法创建了一个基于IPv4和TCP协议的套接字。然后,使用socket.inet_aton()方法将字符串格式的IP地址转换为网络字节序的字节串。最后,使用bind()方法将套接字绑定到指定的IP地址。 值得注意的是,绑定的IP地址必须是当前主机上的一个可用接口的IP地址,否则绑定将失败。在多网卡的环境下,可以通过设置IP地址来选择要使用的网络接口。 通过设置套接字的IP地址,可以使得套接字使用指定的网络接口进行数据的收发,方便网络编程的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值