网络编程入门08

net模块

Callbacks.h

template<typename T>
inline T* get_pointer(const std::shared_ptr<T>& ptr)   //返回shard_ptr的裸指针,感觉意义不大的一个函数
{
  return ptr.get();
}

template<typename T>
inline T* get_pointer(const std::unique_ptr<T>& ptr)  //返回unique_ptr的裸指针
{
  return ptr.get();
}

template<typename To, typename From>                     //对智能指针进行转换
inline ::std::shared_ptr<To> down_pointer_cast(const ::std::shared_ptr<From>& f) {
  if (false)
  {
    implicit_cast<From*, To*>(0);
  }

#ifndef NDEBUG
  assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);
#endif
  return ::std::static_pointer_cast<To>(f);
}

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;                  
typedef std::function<void()> TimerCallback;
typedef std::function<void (const TcpConnectionPtr&)> ConnectionCallback;  //对于一个含有TcpConnection智能指针的对象座位参数的可调用对象,起不同的别名。 可调用对象,就是一种回调函数
typedef std::function<void (const TcpConnectionPtr&)> CloseCallback;
typedef std::function<void (const TcpConnectionPtr&)> WriteCompleteCallback;
typedef std::function<void (const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;

// the data has been read to (buf, len)
typedef std::function<void (const TcpConnectionPtr&,
                            Buffer*,
                            Timestamp)> MessageCallback;

void defaultConnectionCallback(const TcpConnectionPtr& conn);
void defaultMessageCallback(const TcpConnectionPtr& conn,
                            Buffer* buffer,
                            Timestamp receiveTime);

Endian.h

linux系统在endian.h头文件中提供了更多的函数进行主机字节和大小端字节序的相互转换,如下:
//在已经知道自己系统的字节序前提下,调用这些函数会更快点
uint16_t htobe16(uint16_t host_16bits);

uint16_t htole16(uint16_t host_16bits);

uint16_t be16toh(uint16_t big_endian_16bits);

uint16_t le16toh(uint16_t little_endian_16bits);

 

uint32_t htobe32(uint32_t host_32bits);

uint32_t htole32(uint32_t host_32bits);

uint32_t be32toh(uint32_t big_endian_32bits);

uint32_t le32toh(uint32_t little_endian_32bits);

 

uint64_t htobe64(uint64_t host_64bits);

uint64_t htole64(uint64_t host_64bits);

uint64_t be64toh(uint64_t big_endian_64bits);

uint64_t le64toh(uint64_t little_endian_64bits);
inline uint64_t hostToNetwork64(uint64_t host64)  本地到网络字节序转换
{
  return htobe64(host64);   //库函数
}
inline uint32_t hostToNetwork32(uint32_t host32)
{
  return htobe32(host32);
}

inline uint16_t hostToNetwork16(uint16_t host16)
{
  return htobe16(host16);
}

inline uint64_t networkToHost64(uint64_t net64)
{
  return be64toh(net64);
}


inline uint32_t networkToHost32(uint32_t net32)
{
  return be32toh(net32);
}

inline uint16_t networkToHost16(uint16_t net16)
{
  return be16toh(net16);
}

Buffer.h

/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
///
/// @code
/// +-------------------+------------------+------------------+
/// | prependable bytes |  readable bytes  |  writable bytes  |
/// |                   |     (CONTENT)    |                  |
/// +-------------------+------------------+------------------+
/// |                   |                  |                  |
/// 0      <=      readerIndex   <=   writerIndex    <=     size

calss Buffer{
private:
  vector<char> buffer_;
  size_t readerIndex_;
  size_t writerIndex_;
  static const char kCRLF[] = "\r\n"; //在.cc文件里定义的
public:
  static const size_t kCheapPrepend = 8;
  static const size_t kInitialSize = 1024;

  explicit Buffer(size_t initialSize = kInitialSize)
    : buffer_(kCheapPrepend + initialSize),
      readerIndex_(kCheapPrepend),
      writerIndex_(kCheapPrepend)
  {
    assert(readableBytes() == 0);
    assert(writableBytes() == initialSize);
    assert(prependableBytes() == kCheapPrepend);
  }
  void swap(Buffer& rhs);//交换操作 三个变量交换
  size_t readableBytes() const;//可以读的数据量
  size_t writableBytes() const;//可以写的数据量
  size_t prependableBytes() const;//前置数据量
  const char* peek() const;//可以读的点
  const char* findCRLF() const;//在数据区中搜索KCRLF
  {
    const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);
    return crlf == beginWrite() ? NULL : crlf;//找到返回字符串点,找不到返回空
  }
  const char* findEOL() const {  //寻找换行符
       //C 库函数 void *memchr(const void *str, int c, size_t n) 在参数 str 所指向的字符串的前 n 个字节中搜索第一次出现字符 c(一个无符号字符)的位置。
    const void* eol = memchr(peek(), '\n', readableBytes());  
    return static_cast<const char*>(eol);
  }
  const char* findEOL(const char* start) const;//送start处寻找EOL
  void retrieve(size_t len)//取数据
  void retrieveUntil(const char* end)//从数据头到end取数据
  void retrieveInt64();  //就是把读索引前移
  void retrieveInt32()
  void retrieveInt16() 
  void retrieveInt8()
   void retrieveAll()//取所有数据
  string retrieveAllAsString() //取所有数据,返回string
  string retrieveAsString(size_t len);//取一部分数据以string返回
  StringPiece toStringPiece() const;//数据与stringpiece返回
  void append(const char* /*restrict*/ data, size_t len);//写数据
  //还有很多写数据的操作
  int64_t readInt64() {
    int64_t result = peekInt64();  //读Int64_t
    retrieveInt64(); //读索引前移
    return result;// 返回
  }
  int64_t peekInt64() const  //读8个字节的数据
  {
    assert(readableBytes() >= sizeof(int64_t));
    int64_t be64 = 0;
    ::memcpy(&be64, peek(), sizeof be64);
    return sockets::networkToHost64(be64);  //从网络字节序换为本地字节序
  }
  void prependInt8(int8_t x)//加Int8
  {
    prepend(&x, sizeof x);
  }
  void prepend(const void* /*restrict*/ data, size_t len) //往缓冲区前加数据
  {
    assert(len <= prependableBytes());
    readerIndex_ -= len;
    const char* d = static_cast<const char*>(data);
    std::copy(d, d+len, begin()+readerIndex_);
  }
  void shrink(size_t reserve); //在保存未读数据的情况下,预留reserve个字节
  size_t internalCapacity() const //返回缓冲区的大小
  ssize_t readFd(int fd, int* savedErrno) {
      char extrabuf[65536];
      struct iovec vec[2];
      const size_t writable = writableBytes();
      vec[0].iov_base = begin()+writerIndex_;
      vec[0].iov_len = writable;
      vec[1].iov_base = extrabuf;
      vec[1].iov_len = sizeof extrabuf;
      // when there is enough space in this buffer, don't read into extrabuf.
      // when extrabuf is used, we read 128k-1 bytes at most.
      const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1; 
      const ssize_t n = sockets::readv(fd, vec, iovcnt);  //readv和writev函数用于在一次函数调用中读、写多个非连续缓冲区。有时也将这两个函数称为散布读(scatter read)和聚集写    (gather write)。
    /*
    #include <sys/uio.h>
    ssize_t readv(int filedes, const struct iovec *iov, int iovcnt);             //从一个文件描述符,读数据,可能写在多个缓冲区 从0,1,2,3慢慢写
    ssize_t writev(int filedes, const struct iovec *iov, int iovcnt);
    两个函数的返回值:若成功则返回已读、写的字节数,若出错则返回-1
   */
      if (n < 0)
      {
        *savedErrno = errno;
      }
      else if (implicit_cast<size_t>(n) <= writable)
      {
        writerIndex_ += n;
      }
      else      //如果数据比较小的地区,就需要继续从extrabuf读数据
      {
        writerIndex_ = buffer_.size();
        append(extrabuf, n - writable);
      }  
      return n;
  }
}

Timer.h

class Timer{
public:
  Timer(TimerCallback cb, Timestamp when, double interval) 
    : callback_(std::move(cb)),
      expiration_(when),
      interval_(interval),  //间隔启动时间
      repeat_(interval > 0.0),
      sequence_(s_numCreated_.incrementAndGet())           //每次计时器的序号+1
  { }
  void run() const
  {
    callback_();
  }
  Timestamp expiration() const  { return expiration_; }
  bool repeat() const { return repeat_; }
  int64_t sequence() const { return sequence_; }
  void restart(Timestamp now) {
   void Timer::restart(Timestamp now)       
    {
      if (repeat_)
      {
        expiration_ = addTime(now, interval_);
      }
      else
      {
        expiration_ = Timestamp::invalid();
      }
    }

  }

  static int64_t numCreated() { return s_numCreated_.get(); }
private:
  const TimerCallback callback_;  //无参可调用对象
  Timestamp expiration_;         //过期时间
  const double interval_;          
  const bool repeat_;
  const int64_t sequence_;//序号
  static AtomicInt64 s_numCreated_;//原子整数
}

TimerId.h 本身就是对Timer的一个包装

class TimerId {
   private:
    Timer* timer_;  //Timer指针
    int64_t sequence_; timer的序号
   public:
  TimerId()
    : timer_(NULL),
      sequence_(0)
  {
  }

  TimerId(Timer* timer, int64_t seq)
    : timer_(timer),
      sequence_(seq)
  {
  }
  friend class TimerQueue;
}

TimerQueue.h 定时器队列

*timerfd是Linux为用户程序提供的一个定时器接口。这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景。*

一,相关操作函数

#include <sys/timerfd.h>

int timerfd_create(int clockid, int flags) ;
/*
它是用来创建一个定时器描述符timerfd

第一个参数:clockid指定时间类型,有两个值:

CLOCK_REALTIME :Systemwide realtime clock. 系统范围内的实时时钟

CLOCK_MONOTONIC:以固定的速率运行,从不进行调整和复位 ,它不受任何系统time-of-day时钟修改的影响

第二个参数:flags可以是0或者O_CLOEXEC/O_NONBLOCK。

返回值:timerfd(文件描述符)

*/

int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

int timerfd_gettime(int fd, struct itimerspec *curr_value);

struct itimerspec
{
struct timespec it_interval;              //首次超时后,每隔it_interval超时一次
struct timespec it_value;                 //首次超时时间
}



typedef long time_t;
#ifndef _TIMESPEC
#define _TIMESPEC
struct timespec {
time_t tv_sec; // seconds 
long tv_nsec; // and nanoseconds 
};
#endif
struct timespec有两个成员,一个是秒,一个是纳秒, 所以最高精确度是纳秒。
一般由函数int clock_gettime(clockid_t, struct timespec *)获取特定时钟的时间,常用如下4种时钟:
CLOCK_REALTIME 统当前时间,从1970年1.1日算起
CLOCK_MONOTONIC 系统的启动时间,不能被设置
CLOCK_PROCESS_CPUTIME_ID 本进程运行时间
CLOCK_THREAD_CPUTIME_ID 本线程运行时间
int createTimerfd()
{
  int timerfd = ::timerfd_create(CLOCK_MONOTONIC,
                                 TFD_NONBLOCK | TFD_CLOEXEC);
  if (timerfd < 0)
  {
    LOG_SYSFATAL << "Failed in timerfd_create";
  }
  return timerfd;
}

struct timespec howMuchTimeFromNow(Timestamp when)
{
  int64_t microseconds = when.microSecondsSinceEpoch()
                         - Timestamp::now().microSecondsSinceEpoch();
  if (microseconds < 100)
  {
    microseconds = 100;
  }
  struct timespec ts;
  ts.tv_sec = static_cast<time_t>(
      microseconds / Timestamp::kMicroSecondsPerSecond);
  ts.tv_nsec = static_cast<long>(
      (microseconds % Timestamp::kMicroSecondsPerSecond) * 1000);
  return ts;
}


void readTimerfd(int timerfd, Timestamp now)
{
  uint64_t howmany;
  ssize_t n = ::read(timerfd, &howmany, sizeof howmany);
  LOG_TRACE << "TimerQueue::handleRead() " << howmany << " at " << now.toString();
  if (n != sizeof howmany)
  {
    LOG_ERROR << "TimerQueue::handleRead() reads " << n << " bytes instead of 8";
  }
}

/*
int timerfd_settime(int tfd, int flags, const struct itimerspec *newValue, struct itimerspec *oldValue)
功能:  用于启动或关闭指定fd的定时器。
tfd:     timerfd,由timerfd_create函数返回。
flags: 1表示设置的是绝对时间;0表示相对时间。
newValue: 指定新的超时时间,若newValue.it_value非0则启动定时器,否则关闭定时器。若newValue.it_interval为0则定时器只定时一次,否则之后每隔设定时间超时一次。
oldValue:不为NULL时则返回定时器这次设置之前的超时时间。
return:失败则返回-1。
*/

void resetTimerfd(int timerfd, Timestamp expiration)
{
  // wake up loop by timerfd_settime()
  struct itimerspec newValue;
  struct itimerspec oldValue;
  memZero(&newValue, sizeof newValue);
  memZero(&oldValue, sizeof oldValue);
  newValue.it_value = howMuchTimeFromNow(expiration);
  int ret = ::timerfd_settime(timerfd, 0, &newValue, &oldValue);
  if (ret)
  {
    LOG_SYSERR << "timerfd_settime()";
  }
}


calss TimerQueue {
private:
   typedef std::pair<Timestamp, Timer*> Entry;  //时间戳和定时器设为一个pair  命名入口
   typedef std::set<Entry> TimerList; //红黑树实现的一个入口序列
   typedef std::pair<Timer*, int64_t> ActiveTimer;   //Timer指针和Int64_t定义为活跃 定时器
   typedef std::set<ActiveTimer> ActiveTimerSet; //活跃定时器列表

    EventLoop* loop_;

    const int timerfd_;

    Channel timerfdChannel_;

    // Timer list sorted by expiration
    TimerList timers_;

    // for cancel()
    ActiveTimerSet activeTimers_;

    bool callingExpiredTimers_; /* atomic */

    ActiveTimerSet cancelingTimers_;
public:
    explicit TimerQueue(EventLoop* loop)
        : loop_(loop),
        timerfd_(createTimerfd()),
        timerfdChannel_(loop, timerfd_), //channel里一个循环事件指针,一个文件描述符
        timers_(),    //时间戳 + 定时器指针组成的Pair
        callingExpiredTimers_(false)
        {
          timerfdChannel_.setReadCallback(                         //绑定回调函数
          std::bind(&TimerQueue::handleRead, this));
          // we are always reading the timerfd, we disarm it with timerfd_settime.
          timerfdChannel_.enableReading();                   //允许读数据 表明这个频道加入了一个事件循环对象
        }

 
    ~TimerQueue() {
      timerfdChannel_.disableAll();
      timerfdChannel_.remove();       //从事件循环中删除这个频道
      ::close(timerfd_); 
      // do not remove channel, since we're in EventLoop::dtor();
      for (const Entry& timer : timers_)        //删掉每一个di定时器ng
      {
        delete timer.second;
      }
    }
   
    TimerId addTimer(TimerCallback cb,
                   Timestamp when,
                   double interval) 
     {
          Timer* timer = new Timer(std::move(cb), when, interval); //创建一个定时器        
         loop_->runInLoop(
              std::bind(&TimerQueue::addTimerInLoop, this, timer));  //绑定得是addTimerInLoop这个成员函数,参数为timer指针  
          return TimerId(timer, timer->sequence()); 
       }

    void cancel(TimerId timerId) 
   {
          loop_->runInLoop(
          std::bind(&TimerQueue::cancelInLoop, this, timerId));        //新的可调用对象,绑定cancelInLoop函数,参数为timerId
    }


    void addTimerInLoop(Timer* timer)
    {
          loop_->assertInLoopThread();
          bool earliestChanged = insert(timer);     

          if (earliestChanged)   //如果新插入的定时器最早过时,说明最先出发事件的一定是它
          {
            resetTimerfd(timerfd_, timer->expiration());  //把这个时间队列里,过期时间更新,更新为最新的最小值,也就是说这个fd关注的永远是最先过期的事件
          }
    }

    void cancelInLoop(TimerId timerId);
     {
          loop_->assertInLoopThread();
          assert(timers_.size() == activeTimers_.size());
          ActiveTimer timer(timerId.timer_, timerId.sequence_); //指针 + 序号 构造一个activetimer对象
          ActiveTimerSet::iterator it = activeTimers_.find(timer); //在actimeTimer里找
          if (it != activeTimers_.end())
          {
                size_t n = timers_.erase(Entry(it->first->expiration(), it->first)); //时间戳 + 定时器指针, 清除
                assert(n == 1); (void)n;
               delete it->first; // FIXME: no delete please  //删除这个定时器
                activeTimers_.erase(it);  //删除这个 指针+ 序号 在actime set 里对象
          }
          else if (callingExpiredTimers_)
          {
                cancelingTimers_.insert(timer); //加入删除的定时器列表里
          }
          assert(timers_.size() == activeTimers_.size());
    }

    // called when timerfd alarms
    void handleRead()
    {
          loop_->assertInLoopThread();
          Timestamp now(Timestamp::now());
          readTimerfd(timerfd_, now);

          std::vector<Entry> expired = getExpired(now);  //得到所有过期的定时器 返回列表 时间戳+定时器指针

          callingExpiredTimers_ = true;
          cancelingTimers_.clear();
          // safe to callback outside critical section
          for (const Entry& it : expired)    //所有到时间的定时器
          {
            it.second->run();  //调用timer->run()      直接调用定时器当时绑定的回调函数 
          }
          callingExpiredTimers_ = false;

          reset(expired, now);
}


    // move out all expired timers
    std::vector<Entry> getExpired(Timestamp now)
   {
          assert(timers_.size() == activeTimers_.size());
          std::vector<Entry> expired;
          Entry sentry(now, reinterpret_cast<Timer*>(UINTPTR_MAX));
          TimerList::iterator end = timers_.lower_bound(sentry);
          assert(end == timers_.end() || now < end->first);
          std::copy(timers_.begin(), end, back_inserter(expired)); //第一个定时器到end之前的最后一个,插入expried
          timers_.erase(timers_.begin(), end);  //时间戳+指针里的 set

          for (const Entry& it : expired)
          {
                ActiveTimer timer(it.second, it.second->sequence()); //删除 指针 + seq里的数据
                size_t n = activeTimers_.erase(timer);
                assert(n == 1); (void)n;
          }

          assert(timers_.size() == activeTimers_.size());
          return expired;  //返回被删除的数组, 时间戳 + 指针
}



    void reset(const std::vector<Entry>& expired, Timestamp now) 
{
  Timestamp nextExpire;

  for (const Entry& it : expired)
  {
    ActiveTimer timer(it.second, it.second->sequence());
    if (it.second->repeat()           //如果是一个周期任务,计算并且不再取消列表里
        && cancelingTimers_.find(timer) == cancelingTimers_.end())
    {
      it.second->restart(now);
      insert(it.second);
    }
    else
    {
      // FIXME move to a free list
      delete it.second; // FIXME: no delete please   如果单词任务,删除定时器
    }
  }

    bool insert(Timer* timer)
 {
  loop_->assertInLoopThread();
  assert(timers_.size() == activeTimers_.size());
  bool earliestChanged = false;
  Timestamp when = timer->expiration(); //这个定时器的过期时间
  TimerList::iterator it = timers_.begin();
  if (it == timers_.end() || when < it->first)  //如果当前定时器最先到期
  {
    earliestChanged = true; //最早定时器标记设为TRUE
  }
  {
    std::pair<TimerList::iterator, bool> result
      = timers_.insert(Entry(when, timer));      //先插入list
    assert(result.second); (void)result;         
  }
  {
    std::pair<ActiveTimerSet::iterator, bool> result
      = activeTimers_.insert(ActiveTimer(timer, timer->sequence()));  //定时器指针与序号
    assert(result.second); (void)result;
  }

  assert(timers_.size() == activeTimers_.size());
  return earliestChanged;
}
}

转载于:https://www.cnblogs.com/aiqingyi/p/11320256.html

网络编程入门 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统网络编程在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换。 阅读详情

相关推荐

[java][基础]网络编程入门

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统网络编程在网络通信协议下,不同计算机上运行的程序,可以进行数据传输。

一名不太专业的程序员 9522

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

Endian.h 封装了字节序转换函数(全局函数,位于muduo::net::sockets名称空间中)。 SocketsOps.h/ SocketsOps.cc 封装了socket相关系统调用(全局函数,位于muduo::net::sockets名称空间中)。 Socket.h/Socket.cc(Socket类) 用RAII方法封装socket file descriptor。 InetAddress.h/InetAddress.cc(InetAddress类) 网际地址sockaddr_in封装。 1

TABE_的博客 906

C++字节序反转的实现算法(含完整源码)

以上代码中使用了两个函数:HostToNetworkUint32和NetworkToHostUint32,分别用于将主机序列转换为网络序列和将网络序列转换为主机序列。为了解决这个问题,我们需要进行字节序反转,即将大端序转为小端序或者将小端序转换为大端序。值得注意的是,以上代码中针对的是32位整数的字节序反转,如果需要进行其他类型的字节序反转,可以类比上述实现,在函数中调用相应的字节序转换函数即可。通过以上代码实现,我们可以方便地在C++项目中实现字节序反转,从而解决网络通信过程中的字节序问题。

CyberJolt的博客 488

Muduo 网络编程示例之一:五个简单 TCP 协议

这是《Muduo 网络编程示例》系列的第一篇文章。本文将介绍五个简单 TCP 网络服务协议的 muduo 实现,包括 echo (RFC 862)、discard (RFC 863)、chargen (RFC 864)、daytime (RFC 867)、time (RFC 868),以及 time 协议的客户端。以上五个协议使用不同的端口,可以放到同一个进程中实现,且不必使用多线程。

陈硕的Blog 2万+

Java从入门到精通十三(java网络编程)

计算机网络编程就是在我们具体的网络通信协议的规则下,进行实现不同计算机上运行程序的一个数据交换或者说是数据通信的一个过程实现。

jgdabc的博客 5621

4. SAP ABAP OData 服务 Data Provider Class 的 GET_ENTITYSET 方法实现指南

首先第19行CREATEDATA创建er_entityset实例,指定其类型为zcl_zbook_manage_mpc=>tt_book,接着把这个引用映射到刚刚第五行定义的FIELD-SYMBOLS变量上去,这样的话,接下来我们对FIELD-SYMBOLS进行的操作,就会自动反应回引用变量er_entityset.因为我们可能在这个方法里,返回多条图书信息,所以需要用内表类型,而非单条记录的ts_book.ts_book代表一本图书的数据结构。......

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2962

timerfd加epoll实现定时器简单封装

封装了一个使用timerfd和epoll实现的定时器 我的思路是 定时器类初始化时候直接启动一个线程,线程只负责定时事件触发后派发任务 epoll定时器事件触发的时候,将注册的定时事件投递给任务线程 例子中任务线程使用了一个线程池 头文件 #ifndef CCTimer_H_ #define CCTimer_H_ #include "ThreadPool.h" #include <sys/epoll.h> #include <sys/timerfd.h> #includ

veghlreywg的博客 1102

2、机器人控制技术:阻抗/导纳控制与强化学习的融合

本文探讨了阻抗/导纳控制与强化学习在机器人控制中的融合应用。阻抗/导纳控制广泛应用于外骨骼和跟踪控制,通过改进方案提升建模误差下的鲁棒性;强化学习则为解决维度灾难、实现最优交互提供了有效途径,尤其在大输入空间、连续时间系统、冗余机器人及鲁棒控制中表现突出。结合近似器与合理奖励设计,强化学习可在无模型情况下实现高效控制。未来,两者融合有望推动更智能、更安全的机器人系统发展。

ice55的博客 300

Linux从入门到开发实战(C/C++)Day08-Socket网络编程

如果按照之前的tcp测试代码,可以发现s可以连接c1,当c2打开时提示连接服务器成功,但是s收不到c2发来的信息。2.accept函数返回值,创建的socketFd接口对应客户端(一个socket对应一个客户端)1.socket函数创建,创建的socketFd接口对应服务器端的网络设备,用来接受客户端连接。tcp客户端则是只有socket函数创建的socketFd,对应服务器(注意与上面两种进行区分)5.接受客户端连接(accept) 3.连接服务器(connect)

qq_55149044的博客 2276

【电容/电感】电路板会“滋滋”的响,我们称它为啸叫

一、啸叫的起源 大部分硬件工程师应该都遇到过,PCBA上电后出现“滋滋滋”的叫声,其声响或大或小,或时有时无,或深沉或刺耳,或变化无常者皆有。该现象我们称为“啸叫”,一般分为电感啸叫和电容啸叫。

云轩阁 6万+

全桥 LLC 谐振变换器详细解析:结构 + 控制 + 工作原理全搞懂!

在现代高效率电源系统中,LLC 谐振变换器🔥早已成为降损利器。尤其是全桥 LLC 拓扑结构,由于其在、副边整流管的 ZCS(零电流关断),再加上优秀的效率表现,已经成为服务器电源、电动汽车 OBC、光伏逆变等场景的首选。那么,全桥 LLC 到底长啥样?它的控制方式是怎么回事?具体工作波形又是如何呢?今天我们就来一篇讲清楚、讲透彻的文章!🚗💨。

weixin_43199439的博客 3523

Android 网络编程入门指南

在移动应用开发中,网络编程是一个不可或缺的部分。它允许应用与远程服务器进行数据交换,实现数据的上传和下载。本文将为你提供一个关于Android网络编程的入门指南,帮助你理解基本概念和实现步骤。

Unity打怪升级 2330

S7-1200PLC 2轴直线插补详细算法(完整源代码)

速度轴V90PN总线伺服梯形加减速速度控制(标准报文1应用)_v90伺服加减速时间怎么调整-优快云博客文章浏览阅读288次。

RXXW_Dor的博客 4259

谈一谈网络编程学习经验(06-08更新)

谈一谈网络编程学习经验 陈硕 giantchen@gmail.com blog.youkuaiyun.com/Solstice 2011-06-06 PDF 版下载:https://github.com/downloads/chenshuo/documents/LearningNetworkProgramming.pdf本文谈一谈我在学习网络编程方面的一些个人经验。“网络编程”这个术语

陈硕的Blog 14万+

每周技巧tc 问题处理技巧Teamcenter

(内部)Dataset和Tool相关之2.1——Tool轮询Contents Type和Type Name-赖良顺.docx。(内部)Dataset和Tool相关之3.2——编写Batch和Perl为客户端自动注册相关内容-赖良顺.docx。(内部)Dataset和Tool相关之1.1——Tools和Datasets参数定义-赖良顺.docx。(内部)Dataset和Tool相关之1.2——相关首选项定义及修改-赖良顺.docx。(内部)Dataset和Tool相关之0——概述-赖良顺.docx。

TC技术专栏 1230

12、激光制造中人工智能与机器学习的应用及挑战

本文探讨了人工智能与机器学习在激光制造中的应用,重点分析了图像分类技术在质量检测中的多种方法及其性能对比,包括基于贝叶斯分类、卷积神经网络(CNN)和多尺度CNN的技术。同时,文章指出了AI在激光制造中面临的五大挑战:鲁棒性、安全性、可解释性、责任归属和可预测性,并提出了相应的解决思路。此外,还介绍了激光制造的主要应用技术如切割、焊接和增材制造的流程、优势与局限性,并通过对比表格直观展示不同技术的特点。最后展望了未来激光制造向智能化、自动化和可持续化发展的趋势。

rgv234567的博客 138

网络编程入门(一)

本文从网络编程的基础概念出发,详细介绍了Java、Python和C语言中的网络编程实战案例。内容包括TCP和UDP通信的实现、HTTP请求的发送与接收、原始套接字的使用以及简单的Web服务创建。文章还提供了常见问题的解决方案,如处理网络超时、大数据传输和多客户端并发。最后,总结了网络编程的核心知识点,并建议深入学习TCP/IP协议栈、NIO和异步编程,以及探索高级网络框架。

2401_82505179的博客 1550

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

网络编程入门(代码很详细)

网络编程 1.1概述 计算机网络:计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。 网络编程的目的:传播交流信息、数据交换、通信。 想要达到什么效果需要什么: 如何准确定位网络上的某台主机。IP地址+端口。定位到计算机上的某个资源。 找到这台主机,如何传输数据。 网络编程:TCP/IP C/S 1.2、网络通信的要素 如何实现网络的通信: 通信双方的地址: IP

我不想读研的博客 2万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

【带你入门】java网络编程

网络编程网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编程的大门而放弃了对于该部分技术的学习。在 学习网络编程以前,很多初学者可能觉得网络编程是比较复杂的系统工程,需要了解很多和网络相关的基础知识,其实这些都不是很必需的。首先来问一个问题:你 会打手机吗?很多人可能说肯定会啊,不就是按按电话号码,拨打电话嘛,很简单的事情啊!其实初学者如果入门网

好好学java 2万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Linux socket 网络编程入门

Linux下的网络编程一般即是指socket套接字编程,入门比较矮简单,网上也有很多入门的例程。不过每次看过用过以后过段时间又忘了具体的操作了,又得去查,所以在这里总结整理一下,也省了以后查别人教程的时间。1. socket套接字流程简介socket套接字包含标准套接字(SOCK_STREAM,SOCK_DRAGM)以及原始套接字(SOCK_RAW),一般我们进行网络编程有标准套接字就够了,但如果要

u012707739的博客 1460

网络编程

网络编程有什么用呢?这个就不言而喻了,比如我们经常用的微信收发消息就需要用到网络通信的技术、在比如我们打开浏览器可以浏览各种网络、视频等也需要用到网络编程的技术。我们知道什么是网络编程、也知道网络编程能干什么后了,那Java给我们提供了哪些网络编程的解决方案呢?Java提供的网络编程的解决方案都是在java.net包下。在正式学习Java网络编程技术之前,我们还需要学习一些网络通信的前置知识理论知识,只有这些前置知识做基础,我们学习网络编程代码编写才起来才能继续下去。

Xiakeyi的博客 2126

网络编程懒人入门

转自: http://www.52im.net/thread-1095-1-1.html 仅记录到参考文章 ,具体可以点击链接查看 1、写在前面 论坛和群里常会有技术同行打算自已开发IM或者消息推送系统,很多时候连基本的网络编程理论(如网络协议等)都不了解,就贸然定方案、写代码,显得非常盲目且充满技术风险。即时通讯网论坛里精心整理了《[通俗易懂]深入理解TCP协议》、《不为人知的网络编程》、...

shancyan的专栏 366

网络编程入门总结

网络编程是指通过编写程序来实现网络通信的过程。在网络编程中,我们可以使用C语言来实现各种网络应用,如Web服务器、邮件客户端、即时通讯软件等。本章将介绍网络编程的基本概念和常用术语。

极客代码 1443

Python网络编程入门

当今的世界是一个互联的世界,绝大多数的计算机和人都在通过网络和他人传递信息、沟通互联。本讲主要介绍Python 网络编程入门知识。

cnds123的专栏 4010
上一篇: 网络编程入门07
下一篇: 网络编程入门09
aotui2000
博客等级 码龄10年 0粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值