<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>服务端 on 大飞的博客</title>
    <link>https://www.dafei.me/tags/%E6%9C%8D%E5%8A%A1%E7%AB%AF/</link>
    <description>Recent content in 服务端 on 大飞的博客</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Tue, 02 Jun 2026 11:00:00 +0000</lastBuildDate>
    <atom:link href="https://www.dafei.me/tags/%E6%9C%8D%E5%8A%A1%E7%AB%AF/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>从零写OS（四十五）：TCP 服务端——bind/listen/accept</title>
      <link>https://www.dafei.me/posts/os-45-tcp-server/</link>
      <pubDate>Tue, 02 Jun 2026 11:00:00 +0000</pubDate>
      <guid>https://www.dafei.me/posts/os-45-tcp-server/</guid>
      <description>&lt;p&gt;前面几章都是做 TCP 客户端——发 SYN、连接、发请求、收响应。这章反过来，让内核能&lt;strong&gt;作为 TCP 服务端&lt;/strong&gt;：监听端口，接受连接，响应请求。&lt;/p&gt;
&lt;h2 id=&#34;客户端-vs-服务端的区别&#34;&gt;客户端 vs 服务端的区别&lt;/h2&gt;
&lt;p&gt;客户端：主动发 SYN，等待 SYN-ACK。&lt;/p&gt;
&lt;p&gt;服务端：被动等待 SYN，收到后发 SYN-ACK，等 ACK 完成三次握手，然后通过 &lt;code&gt;accept()&lt;/code&gt; 把新连接交给应用层。&lt;/p&gt;
&lt;p&gt;关键区别在于：服务端有两种 socket 角色——&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;角色&lt;/th&gt;
          &lt;th&gt;职责&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;监听 socket（listen fd）&lt;/td&gt;
          &lt;td&gt;绑定端口，等待连接请求&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;连接 socket（accept fd）&lt;/td&gt;
          &lt;td&gt;每个客户端连接对应一个，负责实际数据收发&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;三步接口&#34;&gt;三步接口&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bind&lt;/span&gt;(fd, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;sa, len)    &lt;span style=&#34;color:#75715e&#34;&gt;// 绑定本地端口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;listen&lt;/span&gt;(fd, backlog)   &lt;span style=&#34;color:#75715e&#34;&gt;// 进入监听状态
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;accept&lt;/span&gt;(fd, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;sa, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;len) &lt;span style=&#34;color:#75715e&#34;&gt;// 阻塞，等到有连接，返回新 fd
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;数据结构&#34;&gt;数据结构&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tcp_sock_t&lt;/code&gt; 加了几个字段：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;  is_listener;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;  accept_queue[&lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;];   &lt;span style=&#34;color:#75715e&#34;&gt;// 存已完成三次握手的连接 index
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;uint32_t&lt;/span&gt; accept_head, accept_tail;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;还新增了两个状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;TCP_LISTEN&lt;/code&gt;：监听中，等待 SYN&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TCP_SYN_RECV&lt;/code&gt;：收到 SYN 已回 SYN-ACK，等最终 ACK&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;核心流程&#34;&gt;核心流程&lt;/h2&gt;
&lt;h3 id=&#34;1-收到-syn&#34;&gt;1. 收到 SYN&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tcp_handle()&lt;/code&gt; 里单独处理 SYN：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ((tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_SYN) &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;(tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_ACK)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 找到监听该端口的 socket
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 分配新连接 slot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state       &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TCP_SYN_RECV;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;local_port  &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; dport;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;remote_port &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sport;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;remote_ip   &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; src_ip;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;ack         &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ntohl&lt;/span&gt;(tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;seq) &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;  &lt;span style=&#34;color:#75715e&#34;&gt;// 下次要 ACK 的序号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq     &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0xABCD1234&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;tcp_send_seg&lt;/span&gt;(ns, ns&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq, TCP_SYN &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; TCP_ACK, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-收到最终-ack完成三次握手&#34;&gt;2. 收到最终 ACK（完成三次握手）&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; TCP_SYN_RECV &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; (tcp&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;flags &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; TCP_ACK)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (ack_val &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;syn_seq &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_una &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;snd_nxt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ack_val;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        s&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;state &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; TCP_ESTABLISHED;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 放入监听 socket 的 accept queue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_queue[ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; i;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-accept-取连接&#34;&gt;3. accept() 取连接&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tcp_accept&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; s, ...) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;tcp_sock_t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;ls &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;tcp_socks[s];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;while&lt;/span&gt; (ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_head &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_tail) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sti; &lt;span style=&#34;color:#a6e22e&#34;&gt;net_poll&lt;/span&gt;(); cli;   &lt;span style=&#34;color:#75715e&#34;&gt;// 阻塞等待
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; ni &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_queue[ls&lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt;accept_head&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;8&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; ni;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;测试&#34;&gt;测试&lt;/h2&gt;
&lt;p&gt;写了一个简单的 HTTP 服务端程序：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
