<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>TCP归档 - 枫阿雨&#039;s blog</title>
	<atom:link href="https://www.crazyfay.com/tag/tcp/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.crazyfay.com/tag/tcp/</link>
	<description>CrazyFay</description>
	<lastBuildDate>Mon, 03 Apr 2023 17:03:18 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.5.2</generator>

<image>
	<url>https://www.crazyfay.com/wp-content/uploads/2023/04/cropped-DockerGopher-32x32.png</url>
	<title>TCP归档 - 枫阿雨&#039;s blog</title>
	<link>https://www.crazyfay.com/tag/tcp/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Golang实现支持中间件的简易TCP框架</title>
		<link>https://www.crazyfay.com/2022/08/16/golang%e5%ae%9e%e7%8e%b0%e6%94%af%e6%8c%81%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%9a%84%e7%ae%80%e6%98%93tcp%e6%a1%86%e6%9e%b6/</link>
					<comments>https://www.crazyfay.com/2022/08/16/golang%e5%ae%9e%e7%8e%b0%e6%94%af%e6%8c%81%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%9a%84%e7%ae%80%e6%98%93tcp%e6%a1%86%e6%9e%b6/#respond</comments>
		
		<dc:creator><![CDATA[crazyfay]]></dc:creator>
		<pubDate>Tue, 16 Aug 2022 07:48:29 +0000</pubDate>
				<category><![CDATA[代码实战]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[TCP]]></category>
		<category><![CDATA[网络]]></category>
		<guid isPermaLink="false">http://net.crazyfay.xyz/?p=138</guid>

					<description><![CDATA[<p>在golang的标准库中没有为tcp直接提供像http那样简单易用的服务框架，我们不妨自己手动实现一个 主体思 [&#8230;]</p>
<p><a rel="nofollow" href="https://www.crazyfay.com/2022/08/16/golang%e5%ae%9e%e7%8e%b0%e6%94%af%e6%8c%81%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%9a%84%e7%ae%80%e6%98%93tcp%e6%a1%86%e6%9e%b6/">Golang实现支持中间件的简易TCP框架</a>最先出现在<a rel="nofollow" href="https://www.crazyfay.com">枫阿雨&#039;s blog</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>在golang的标准库中没有为tcp直接提供像http那样简单易用的服务框架，我们不妨自己手动实现一个</p>
<h2>主体思路</h2>
<p>我们的实现的主题思路分为以下四个内容</p>
<ol>
<li>监听服务</li>
<li>获取构建新连接对象并设置超时时间及keepalive</li>
<li>设置方法退出时连接关闭</li>
<li>调用回调接口 TcpHandler</li>
</ol>
<h2>主要结构体和接口</h2>
<p>首先是TCPServer的结构体，我们希望用户可以自由构建TcpServer并设置超时时间等自定义选项</p>
<pre><code class="language-go">type TcpServer struct {
   Addr    string
   Handler TCPHandler  &lt;- 对外提供的服务方法接口
   err     error
   BaseCtx context.Context

   WriteTimeout     time.Duration
   ReadTimeout      time.Duration
   KeepAliveTimeout time.Duration

   mu         sync.Mutex
   inShutdown int32
   doneChan   chan struct{}
   l          *onceCloseListener
}</code></pre>
<p>像httpHandler一样，对外提供抽象的ServeTCP方法</p>
<pre><code class="language-go">type TCPHandler interface {
   ServeTCP(ctx context.Context, conn net.Conn)
}</code></pre>
<h2>服务启动方法</h2>
<p>用户可以通过自行构建TcpServer实例再通过ListenAndServe()调用服务，或通过<code>tcp.ListenAndServe(&quot;:8080&quot;, handler)</code> 使用默认的TcpServer实例快速启动服务。</p>
<p>在 <code>ListenAndServe()</code> 方法中，进行参数的校验和初始化操作 </p>
<p><code>Serve(l net.Listener)</code> 方法中，通过 <code>l.Accept()</code> 接收信息，包装接收到的conn并另起一个协程处理服务</p>
<pre><code class="language-go">func ListenAndServe(addr string, handler TCPHandler) error {
    server := &amp;TcpServer{Addr: addr, Handler: handler, doneChan: make(chan struct{})}
    return server.ListenAndServe()
}

func (s *TcpServer) ListenAndServe() error {
   if s.shuttingDown() {
      return ErrServerClosed
   }
   if s.doneChan == nil {
      s.doneChan = make(chan struct{})
   }
   addr := s.Addr
   if addr == &quot;&quot; {
      return errors.New(&quot;need addr&quot;)
   }
   ln, err := net.Listen(&quot;tcp&quot;, addr)
   if err != nil {
      return err
   }
   return s.Serve(tcpKeepAliveListener{
      ln.(*net.TCPListener)})
}

func (s *TcpServer) Serve(l net.Listener) error {
    s.l = &amp;onceCloseListener{Listener: l}
    defer s.l.Close() //执行listener关闭
    if s.BaseCtx == nil {
        s.BaseCtx = context.Background()
    }
    baseCtx := s.BaseCtx
    ctx := context.WithValue(baseCtx, ServerContextKey, s) &lt;- 将TcpServer实例存入context中
    for {
        rw, e := l.Accept()
        if e != nil {
            select {
            case &lt;-s.getDoneChan():
                return ErrServerClosed
            default:
            }
            fmt.Printf(&quot;accept fail, err: %v\n&quot;, e)
            continue
        }
        c := s.newConn(rw)
        go c.serve(ctx)
    }
    return nil
}</code></pre>
<p>包装 <code>net.Conn</code> 为 <code>tcp.conn</code> </p>
<pre><code class="language-go">type conn struct {
    server     *TcpServer   // 反引用TcpServer
    remoteAddr string       // 发送端地址
    rwc        net.Conn
}

func (s *TcpServer) newConn(rwc net.Conn) *conn {
   c := &amp;conn{
      server: s,
      rwc:    rwc,
   }
   // 设置参数
   if d := c.server.ReadTimeout; d != 0 {
      c.rwc.SetReadDeadline(time.Now().Add(d))
   }
   if d := c.server.WriteTimeout; d != 0 {
      c.rwc.SetWriteDeadline(time.Now().Add(d))
   }
   if d := c.server.KeepAliveTimeout; d != 0 {
      if tcpConn, ok := c.rwc.(*net.TCPConn); ok {
         tcpConn.SetKeepAlive(true)
         tcpConn.SetKeepAlivePeriod(d)
      }
   }
   return c
}</code></pre>
<p>由 <code>tcp.conn.Server(ctx)</code> 调用回调函数进行服务处理</p>
<pre><code class="language-go">func (c *conn) serve(ctx context.Context) {
   defer func() {
      if err := recover(); err != nil &amp;&amp; err != ErrAbortHandler {
         const size = 64 &lt;&lt; 10
         buf := make([]byte, size)
         buf = buf[:runtime.Stack(buf, false)]
         fmt.Printf(&quot;tcp: panic serving %v: %v\n%s&quot;, c.remoteAddr, err, buf)
      }
      c.close()
   }()
   c.remoteAddr = c.rwc.RemoteAddr().String()
   ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
   if c.server.Handler == nil {
      panic(&quot;handler empty&quot;)
   }
   c.server.Handler.ServeTCP(ctx, c.rwc)
}</code></pre>
<p>这样，一个简单易用的TCP服务框架就搭建完成了，其中一些<code>close()</code> 等方法在此处没有展示出来，更多详细代码可在我的代码仓库中查看：<a href="https://github.com/Kirov7/fayUtils/net/tcp">https://github.com/Kirov7/fayUtils/net/tcp</a></p>
<h2>扩展中间件的实现</h2>
<p>扩展中间件功能的实现思路</p>
<ul>
<li>方法构建
<ul>
<li>构建中间件URL路由</li>
<li>构建URL的中间件方法数组</li>
<li>使用Use方法整合路由与方法数组</li>
</ul>
</li>
<li>方法调用
<ul>
<li>构建方法请求逻辑</li>
<li>封装TCPHandler接口与TcpServer整合</li>
</ul>
</li>
</ul>
<p><code>TcpSliceRouter.Group(path)</code> 方法，初始化路由分组（默认只能全局）</p>
<pre><code class="language-go">// 创建 Group
func (g *TcpSliceRouter) Group(path string) *TcpSliceGroup {
   if path != &quot;/&quot; {
      panic(&quot;only accept path=/&quot;)
   }
   return &amp;TcpSliceGroup{
      TcpSliceRouter: g,
      path:           path,
   }
}</code></pre>
<p><code>TcpSliceGroup.Use(middlewares ...TcpHandlerFunc)</code> 构造回调方法</p>
<p>调用 <code>Use</code> 方法传入中间件集合，添加到切片 <code>c.handlers</code> 中</p>
<pre><code class="language-go">// 构造回调方法
func (g *TcpSliceGroup) Use(middlewares ...TcpHandlerFunc) *TcpSliceGroup {
   g.handlers = append(g.handlers, middlewares...)
   existsFlag := false
   for _, oldGroup := range g.TcpSliceRouter.groups {
      if oldGroup == g {
         existsFlag = true
      }
   }
   if !existsFlag {
      g.TcpSliceRouter.groups = append(g.TcpSliceRouter.groups, g)
   }
   return g
}</code></pre>
<p>通过 <code>NewTcpSliceRouterHandler</code> 方法传入最后调用的逻辑方法<code>coreFunc</code>并传入已经 <code>Use</code> 了中间件的， <code>TcpSliceRouter</code> </p>
<pre><code class="language-go">func NewTcpSliceRouterHandler(coreFunc func(*TcpSliceRouterContext) tcp_server.TCPHandler, router *TcpSliceRouter) *TcpSliceRouterHandler {
   return &amp;TcpSliceRouterHandler{
      coreFunc: coreFunc,
      router:   router,
   }
}</code></pre>
<p>最终的回调函数 <code>ServeTCP((ctx context.Context, conn net.Conn)</code>，初始化 <code>context</code> 之后将 <code>coreFunc</code> 追加到 <code>c.handlers</code>，重置执行光标，从第一个 <code>c.handlers</code> 开始执行中间件</p>
<pre><code class="language-go">func (w *TcpSliceRouterHandler) ServeTCP(ctx context.Context, conn net.Conn) {
   c := newTcpSliceRouterContext(conn, w.router, ctx)
   c.handlers = append(c.handlers, func(c *TcpSliceRouterContext) {
      w.coreFunc(c).ServeTCP(ctx, conn)
   })
   c.Reset()
   c.Next()
}</code></pre>
<p>在中间件中自行调用<code>Next()</code>、<code>Abort()</code>等中间件逻辑，最后所有中间件执行完毕之后执行 <code>coreFunc</code>（已经被追加到<code>c.handlers</code>的最后位置）</p>
<pre><code class="language-go">// 从最先加入中间件开始回调
func (c *TcpSliceRouterContext) Next() {
   c.index++
   for c.index &lt; int8(len(c.handlers)) {
      c.handlers[c.index](c)
      c.index++
   }
}

// 跳出中间件方法
func (c *TcpSliceRouterContext) Abort() {
    c.index = abortIndex
}</code></pre>
<p><a rel="nofollow" href="https://www.crazyfay.com/2022/08/16/golang%e5%ae%9e%e7%8e%b0%e6%94%af%e6%8c%81%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%9a%84%e7%ae%80%e6%98%93tcp%e6%a1%86%e6%9e%b6/">Golang实现支持中间件的简易TCP框架</a>最先出现在<a rel="nofollow" href="https://www.crazyfay.com">枫阿雨&#039;s blog</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.crazyfay.com/2022/08/16/golang%e5%ae%9e%e7%8e%b0%e6%94%af%e6%8c%81%e4%b8%ad%e9%97%b4%e4%bb%b6%e7%9a%84%e7%ae%80%e6%98%93tcp%e6%a1%86%e6%9e%b6/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
