検索
ホームページバックエンド開発Golangi/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります

質問

毎日のコードを見てみましょう。

 1package main
 2
 3import (
 4    "bytes"
 5    "encoding/json"
 6    "fmt"
 7    "io/ioutil"
 8    "net"
 9    "net/http"
10    "time"
11)
12
13var tr *http.Transport
14
15func init() {
16    tr = &http.Transport{
17        MaxIdleConns: 100,
18        Dial: func(netw, addr string) (net.Conn, error) {
19            conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
20            if err != nil {
21                return nil, err
22            }
23            err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
24            if err != nil {
25                return nil, err
26            }
27            return conn, nil
28        },
29    }
30}
31
32func main() {
33    for {
34        _, err := Get("http://www.baidu.com/")
35        if err != nil {
36            fmt.Println(err)
37            break
38        }
39    }
40}
41
42
43func Get(url string) ([]byte, error) {
44    m := make(map[string]interface{})
45    data, err := json.Marshal(m)
46    if err != nil {
47        return nil, err
48    }
49    body := bytes.NewReader(data)
50    req, _ := http.NewRequest("Get", url, body)
51    req.Header.Add("content-type", "application/json")
52
53    client := &http.Client{
54        Transport: tr,
55    }
56    res, err := client.Do(req)
57    if res != nil {
58        defer res.Body.Close()
59    }
60    if err != nil {
61        return nil, err
62    }
63    resBody, err := ioutil.ReadAll(res.Body)
64    if err != nil {
65        return nil, err
66    }
67    return resBody, nil
68}

が行うことは比較的単純で、ループして http://www.baidu.com/ を要求し、応答を待つことです。

問題ないようです。

コードを実行すると、 実際に通常どおりメッセージを送受信できます

しかし、このコードが 一定期間実行されると、i/o timeout エラーが表示されます。


これは実は最近調査された問題で、この落とし穴に踏みやすい可能性があることがわかったので、ここでコードを簡略化しました。

実際の運用環境で発生する

現象 は、http.Transport# にもかかわらず、golang サービスが http 呼び出しを開始することです。 ##3s タイムアウトが設定されている場合、i/o timeout エラーが表示されることがあります。 しかし、ダウンストリーム サービスを確認したところ、実際にはダウンストリーム サービスが

100ms

で戻ってきたことがわかりました。

トラブルシューティング

i/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります
5 層ネットワーク プロトコルに対応するメッセージ本文の変更の分析

非常に奇妙です、サーバー処理時間は 100ms のみで、クライアント タイムアウトは 3s に設定されているのに、タイムアウト エラー i/o timeout が表示されるのはなぜですか?


#ここには 2 つの可能性があります。

  • サーバーによって出力されるログは、実際には

    サーバー アプリケーション層によって出力されるログのみであるためです。ただし、クライアント アプリケーション層がデータを送信した後、データは クライアントのトランスポート層、ネットワーク層、データリンク層、物理層 を通過し、さらに サーバーの物理層、データリンク層、ネットワークも通過します。サーバー側の層、トランスポート層からアプリケーション層まで。サーバー アプリケーション層で 100ms かかり、その後元のパスに戻ります。残りの 3 秒~100 ミリ秒 は、プロセス全体のさまざまなレイヤーで 無駄になる可能性があります。たとえば、ネットワークが良好でない場合、トランスポート層の TCP はパケットを激しく失い、再送信します。

  • ネットワークに問題はありません。クライアントからサーバーまでの送受信プロセス全体には、おそらく

    100ms 程度かかります。クライアント処理ロジックの問題によりタイムアウトが発生します。


通常、問題が発生した場合、ほとんどの場合、それは基盤となるネットワークの問題ではありません。あなた自身の問題、あなたは正しいです。、諦めたくないなら、バッグを手に取って見てください。

i/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります

パケット キャプチャ結果
分析後、3 回のハンドシェイクの最初から (

赤いボックスが表示されている場所)描かれています## #)。

到最后出现超时报错 i/o timeout画了蓝框的地方)。

time那一列从710,确实间隔3s。而且看右下角的蓝框,是51169端口发到80端口的一次Reset连接。

80端口是服务端的端口。换句话说就是客户端3s超时主动断开链接的。

但是再仔细看下第一行三次握手到最后客户端超时主动断开连接的中间,其实有非常多次HTTP请求

回去看代码设置超时的方式。

 1    tr = &http.Transport{
 2        MaxIdleConns: 100,
 3        Dial: func(netw, addr string) (net.Conn, error) {
 4            conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
 5            if err != nil {
 6                return nil, err
 7            }
 8            err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
 9            if err != nil {
10                return nil, err
11            }
12            return conn, nil
13        },
14    }

也就是说,这里的3s超时,其实是在建立连接之后开始算的,而不是单次调用开始算的超时。

看注释里写的是

SetDeadline sets the read and write deadlines associated with the connection.



タイムアウトの理由

誰もが知っているように、HTTP はアプリケーション層プロトコルであり、トランスポート層は TCP プロトコルを使用します。

HTTP プロトコルは、1.0 以降、デフォルトで short connection を使用しており、リクエストが行われるたびに TCP 接続が確立されます。データを送受信します。その後、接続を解除します。

すべての TCP 接続は 3 ウェイ ハンドシェイクです。それぞれの切断には 4 つの波が必要です。

実際には、毎回新しい接続を確立する必要はなく、確立された接続は切断されない限り、データを送信するたびに再利用されます。

それ以来、HTTP プロトコルは 1.1 以降、デフォルトで 長い接続を使用するようになりました。具体的な情報については、この前の記事を参照してください。

となると、golang 標準ライブラリ もこの実装と互換性があります。

接続プールを確立することで、http://baidu.comhttp:/ などの 各ドメイン名 に対して TCP ロング接続を確立します。 / golang.com は 2 つの異なるドメイン名です。

http://baidu.com に初めてアクセスすると、接続が確立され、使い果たされると、アイドル接続プールに配置されます。 http://baidu.com にアクセスすると、接続プール reuse からこの接続が取得されます。

i/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります
長い接続を再利用する


余談を挿入します。これは、この記事の最後のステートメントについても説明しています。同じドメイン名であることを強調します。ドメイン名は接続を確立し、1 つの接続は 読み取りゴルーチンと書き込みゴルーチン に対応します。同じドメイン名なので最終的には 3 個のゴルーチンが漏洩しますが、異なるドメイン名であれば 1 2*N 個のゴルーチンが漏洩します、N はドメイン名の数です。


最初のリクエストに 100ms かかると仮定し、各リクエストの後に http://baidu.com を配置します。すべての 接続プールに追加し、次回も再利用し続けます。これを 29 回繰り返し、2900ms かかります。

30 リクエストの時点で、接続は確立されてからサービスが返されるまでにすでに 3000ms かかっており、設定された # にちょうど到達しました。 ##3s タイムアウトしきい値を設定すると、クライアントはタイムアウト i/o タイムアウト を報告します。

この時点でサーバーは実際には

100ms かかっていましたが、前回の 29 回 には耐えられず、合計時間はすでに非常に長くなってしまいました。

也就是说只要通过 http.Transport 设置了 err = conn.SetDeadline(time.Now().Add(time.Second * 3)),并且你用了长连接,哪怕服务端处理再快,客户端设置的超时再长,总有一刻,你的程序会报超时错误。

正确姿势

原本预期是给每次调用设置一个超时,而不是给整个连接设置超时。

另外,上面出现问题的原因是给长连接设置了超时,且长连接会复用。

基于这两点,改一下代码。

 1package main
 2
 3import (
 4    "bytes"
 5    "encoding/json"
 6    "fmt"
 7    "io/ioutil"
 8    "net/http"
 9    "time"
10)
11
12var tr *http.Transport
13
14func init() {
15    tr = &http.Transport{
16        MaxIdleConns: 100,
17        // 下面的代码被干掉了
18        //Dial: func(netw, addr string) (net.Conn, error) {
19        //  conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
20        //  if err != nil {
21        //      return nil, err
22        //  }
23        //  err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
24        //  if err != nil {
25        //      return nil, err
26        //  }
27        //  return conn, nil
28        //},
29    }
30}
31
32
33func Get(url string) ([]byte, error) {
34    m := make(map[string]interface{})
35    data, err := json.Marshal(m)
36    if err != nil {
37        return nil, err
38    }
39    body := bytes.NewReader(data)
40    req, _ := http.NewRequest("Get", url, body)
41    req.Header.Add("content-type", "application/json")
42
43    client := &http.Client{
44        Transport: tr,
45        Timeout: 3*time.Second,  // 超时加在这里,是每次调用的超时
46    }
47    res, err := client.Do(req) 
48    if res != nil {
49        defer res.Body.Close()
50    }
51    if err != nil {
52        return nil, err
53    }
54    resBody, err := ioutil.ReadAll(res.Body)
55    if err != nil {
56        return nil, err
57    }
58    return resBody, nil
59}
60
61func main() {
62    for {
63        _, err := Get("http://www.baidu.com/")
64        if err != nil {
65            fmt.Println(err)
66            break
67        }
68    }
69}

看注释会发现,改动的点有两个

  • http.Transport里的建立连接时的一些超时设置干掉了。

  • 在发起http请求的时候会场景http.Client,此时加入超时设置,这里的超时就可以理解为单次请求的超时了。同样可以看下注释

Timeout specifies a time limit for requests made by this Client.

到这里,代码就改好了,实际生产中问题也就解决了。

实例代码里,如果拿去跑的话,其实还会下面的错

1Get http://www.baidu.com/: EOF

这个是因为调用得太猛了,http://www.baidu.com 那边主动断开的连接,可以理解为一个限流措施,目的是为了保护服务器,毕竟每个人都像这么搞,服务器是会炸的。。。

解决方案很简单,每次HTTP调用中间加个sleep间隔时间就好。


到这里,其实问题已经解决了,下面会在源码层面分析出现问题的原因。对读源码不感兴趣的朋友们可以不用接着往下看,直接拉到文章底部右下角,做点正能量的事情(点两下)支持一下。(疯狂暗示,拜托拜托,这对我真的很重要!

源码分析

用的go版本是1.12.7

从发起一个网络请求开始跟。

 1res, err := client.Do(req)
 2func (c *Client) Do(req *Request) (*Response, error) {
 3    return c.do(req)
 4}
 5
 6func (c *Client) do(req *Request) {
 7    // ...
 8    if resp, didTimeout, err = c.send(req, deadline); err != nil {
 9    // ...
10  }
11    // ...  
12}  
13func send(ireq *Request, rt RoundTripper, deadline time.Time) {
14    // ...    
15    resp, err = rt.RoundTrip(req)
16     // ...  
17} 
18
19// 从这里进入 RoundTrip 逻辑
20/src/net/http/roundtrip.go: 16
21func (t *Transport) RoundTrip(req *Request) (*Response, error) {
22    return t.roundTrip(req)
23}
24
25func (t *Transport) roundTrip(req *Request) (*Response, error) {
26    // 尝试去获取一个空闲连接,用于发起 http 连接
27  pconn, err := t.getConn(treq, cm)
28  // ...
29}
30
31// 重点关注这个函数,返回是一个长连接
32func (t *Transport) getConn(treq *transportRequest, cm connectMethod) (*persistConn, error) {
33  // 省略了大量逻辑,只关注下面两点
34    // 有空闲连接就返回
35    pc := <-t.getIdleConnCh(cm)
36
37  // 没有创建连接
38  pc, err := t.dialConn(ctx, cm)
39
40}

这里上面很多代码,其实只是为了展示这部分代码是怎么跟踪下来的,方便大家去看源码的时候去跟一下。

最后一个上面的代码里有个 getConn 方法。在发起网络请求的时候,会先取一个网络连接,取连接有两个来源。

  • 如果有空闲连接,就拿空闲连接

    1/src/net/http/tansport.go:810
    2func (t *Transport) getIdleConnCh(cm connectMethod) chan *persistConn {
    3 // 返回放空闲连接的chan
    4 ch, ok := t.idleConnCh[key]
    5   // ...
    6 return ch
    7}
  • 没有空闲连接,就创建长连接。

1/src/net/http/tansport.go:1357
2func (t *Transport) dialConn() {
3  //...
4  conn, err := t.dial(ctx, "tcp", cm.addr())
5  // ...
6  go pconn.readLoop()
7  go pconn.writeLoop()
8  // ...
9}

第一次发起一个http请求时,这时候肯定没有空闲连接,会建立一个新连接。同时会创建一个读goroutine和一个写goroutine

i/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります
读写协程

注意上面代码里的t.dial(ctx, "tcp", cm.addr()),如果像文章开头那样设置了 http.Transport

 1Dial: func(netw, addr string) (net.Conn, error) {
 2   conn, err := net.DialTimeout(netw, addr, time.Second*2) //设置建立连接超时
 3   if err != nil {
 4      return nil, err
 5   }
 6   err = conn.SetDeadline(time.Now().Add(time.Second * 3)) //设置发送接受数据超时
 7   if err != nil {
 8      return nil, err
 9   }
10   return conn, nil
11},

那么这里就会在下面的dial里被执行到

1func (t *Transport) dial(ctx context.Context, network, addr string) (net.Conn, error) {
2   // ...
3  c, err := t.Dial(network, addr)
4  // ...
5}

这里面调用的设置超时,会执行到

 1/src/net/net.go
 2func (c *conn) SetDeadline(t time.Time) error {
 3    //...
 4    c.fd.SetDeadline(t)
 5    //...
 6}
 7
 8//...
 9
10func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
11    // ...
12    runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
13    return nil
14}
15
16
17//go:linkname poll_runtime_pollSetDeadline internal/poll.runtime_pollSetDeadline
18func poll_runtime_pollSetDeadline(pd *pollDesc, d int64, mode int) {
19    // ...
20  // 设置一个定时器事件
21  rtf = netpollDeadline
22    // 并将事件注册到定时器里
23  modtimer(&pd.rt, pd.rd, 0, rtf, pd, pd.rseq)
24}

上面的源码,简单来说就是,当第一次调用请求的,会建立个连接,这时候还会注册一个定时器事件,假设时间设了3s,那么这个事件会在3s后发生,然后执行注册事件的逻辑。而这个注册事件就是netpollDeadline注意这个netpollDeadline,待会会提到。

i/o タイムアウト、この net/http パッケージの罠に踏まないことを祈ります
读写协程定时器事件

设置了超时事件,且超时事件是3s后之后,发生。再次期间正常收发数据。一切如常。

直到3s过后,这时候看读goroutine,会等待网络数据返回。

1/src/net/http/tansport.go:1642
2func (pc *persistConn) readLoop() {
3    //...
4    for alive {
5        _, err := pc.br.Peek(1)  // 阻塞读取服务端返回的数据
6    //...
7}

然后就是一直跟代码。

 1src/bufio/bufio.go: 129
 2func (b *Reader) Peek(n int) ([]byte, error) {
 3   // ...
 4   b.fill() 
 5   // ...   
 6}
 7
 8func (b *Reader) fill() {
 9    // ...
10    n, err := b.rd.Read(b.buf[b.w:])
11    // ...
12}
13
14/src/net/http/transport.go: 1517
15func (pc *persistConn) Read(p []byte) (n int, err error) {
16    // ...
17    n, err = pc.conn.Read(p)
18    // ...
19}
20
21// /src/net/net.go: 173
22func (c *conn) Read(b []byte) (int, error) {
23    // ...
24    n, err := c.fd.Read(b)
25    // ...
26}
27
28func (fd *netFD) Read(p []byte) (n int, err error) {
29    n, err = fd.pfd.Read(p)
30    // ...
31}
32
33/src/internal/poll/fd_unix.go: 
34func (fd *FD) Read(p []byte) (int, error) {
35    //...
36  if err = fd.pd.waitRead(fd.isFile); err == nil {
37    continue
38  }
39    // ...
40}
41
42func (pd *pollDesc) waitRead(isFile bool) error {
43    return pd.wait(&#39;r&#39;, isFile)
44}
45
46func (pd *pollDesc) wait(mode int, isFile bool) error {
47    // ...
48  res := runtime_pollWait(pd.runtimeCtx, mode)
49    return convertErr(res, isFile)
50}

直到跟到 runtime_pollWait,这个可以简单认为是等待服务端数据返回

 1//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait
 2func poll_runtime_pollWait(pd *pollDesc, mode int) int {
 3
 4    // 1.如果网络正常返回数据就跳出
 5  for !netpollblock(pd, int32(mode), false) {
 6    // 2.如果有出错情况也跳出
 7        err = netpollcheckerr(pd, int32(mode))
 8        if err != 0 {
 9            return err
10        }
11    }
12    return 0
13}

整条链路跟下来,就是会一直等待数据,等待的结果只有两个

  • 有可以读的数据

  • 出现报错

这里面的报错,又有那么两种

  • 连接关闭

  • 超时

1func netpollcheckerr(pd *pollDesc, mode int32) int {
2    if pd.closing {
3        return 1 // errClosing
4    }
5    if (mode == &#39;r&#39; && pd.rd < 0) || (mode == &#39;w&#39; && pd.wd < 0) {
6        return 2 // errTimeout
7    }
8    return 0
9}

其中提到的超时,就是指这里面返回的数字2,会通过下面的函数,转化为 ErrTimeout, 而 ErrTimeout.Error() 其实就是 i/o timeout

 1func convertErr(res int, isFile bool) error {
 2    switch res {
 3    case 0:
 4        return nil
 5    case 1:
 6        return errClosing(isFile)
 7    case 2:
 8        return ErrTimeout // ErrTimeout.Error() 就是 "i/o timeout"
 9    }
10    println("unreachable: ", res)
11    panic("unreachable")
12}

那么问题来了。上面返回的超时错误,也就是返回2的时候的条件是怎么满足的

1    if (mode == &#39;r&#39; && pd.rd < 0) || (mode == &#39;w&#39; && pd.wd < 0) {
2        return 2 // errTimeout
3    }

还记得刚刚提到的 netpollDeadline吗?

这里面放了定时器3s到点时执行的逻辑。

 1func timerproc(tb *timersBucket) {
 2    // 计时器到设定时间点了,触发之前注册函数
 3    f(arg, seq) // 之前注册的是 netpollDeadline
 4}
 5
 6func netpollDeadline(arg interface{}, seq uintptr) {
 7    netpolldeadlineimpl(arg.(*pollDesc), seq, true, true)
 8}
 9
10/src/runtime/netpoll.go: 428
11func netpolldeadlineimpl(pd *pollDesc, seq uintptr, read, write bool) {
12    //...
13    if read {
14        pd.rd = -1
15        rg = netpollunblock(pd, &#39;r&#39;, false)
16    }
17    //...
18}

这里会设置pd.rd=-1,是指 poller descriptor.read deadline ,含义网络轮询器文件描述符读超时时间, 我们知道在linux里万物皆文件,这里的文件其实是指这次网络通讯中使用到的socket

这时候再回去看发生超时的条件就是if (mode == 'r' && pd.rd 。

至此。我们的代码里就收到了 io timeout 的报错。

概要

  • http.Transport でタイムアウトを設定しないでください。これは、接続のタイムアウトではなく、接続のタイムアウトです。リクエスト。そうしないと、不可解な io timeout エラーが発生する可能性があります。

  • リクエストのタイムアウトは、作成 client で設定されます。

以上がi/o タイムアウト、この net/http パッケージの罠に踏まないことを祈りますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事はGo语言进阶学习で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
GolangとPythonの選択:プロジェクトに適していますGolangとPythonの選択:プロジェクトに適していますApr 19, 2025 am 12:21 AM

golangisidealforporformance-criticalapplicationsandconcurrentprogramming、whilepythonexcelsindatascience、rapyプロトタイプ、およびandversitielity.1)for-high-duetoitsefficiency and concurrencyfeatures.2

Golang:並行性と行動のパフォーマンスGolang:並行性と行動のパフォーマンスApr 19, 2025 am 12:20 AM

GolangはGoroutineとChannelを通じて効率的な並行性を実現します。1。Goroutineは、Goキーワードで始まる軽量のスレッドです。 2.チャンネルは、ゴルチン間の安全な通信に使用され、人種の状態を避けます。 3.使用例は、基本的および高度な使用法を示しています。 4.一般的なエラーには、ゴルンレースで検出できるデッドロックとデータ競争が含まれます。 5.パフォーマンスの最適化では、チャネルの使用を削減し、ゴルチンの数を合理的に設定し、Sync.poolを使用してメモリを管理することを示唆しています。

Golang vs. Python:どの言語を学ぶべきですか?Golang vs. Python:どの言語を学ぶべきですか?Apr 19, 2025 am 12:20 AM

Golangは、システムプログラミングと高い並行性アプリケーションにより適していますが、Pythonはデータサイエンスと迅速な発展により適しています。 1)GolangはGoogleによって開発され、静的にタイピングし、シンプルさと効率を強調しており、高い並行性シナリオに適しています。 2)Pythonは、Guidovan Rossumによって作成され、動的に型付けられた簡潔な構文、幅広いアプリケーション、初心者やデータ処理に適しています。

Golang vs. Python:パフォーマンスとスケーラビリティGolang vs. Python:パフォーマンスとスケーラビリティApr 19, 2025 am 12:18 AM

Golangは、パフォーマンスとスケーラビリティの点でPythonよりも優れています。 1)Golangのコンピレーションタイプの特性と効率的な並行性モデルにより、高い並行性シナリオでうまく機能します。 2)Pythonは解釈された言語として、ゆっくりと実行されますが、Cythonなどのツールを介してパフォーマンスを最適化できます。

Golang vs.その他の言語:比較Golang vs.その他の言語:比較Apr 19, 2025 am 12:11 AM

GO言語は、同時プログラミング、パフォーマンス、学習曲線などにユニークな利点を持っています。1。GoroutineとChannelを通じて同時プログラミングが実現されます。これは軽量で効率的です。 2。コンピレーション速度は高速で、操作性能はC言語のパフォーマンスに近いです。 3.文法は簡潔で、学習曲線は滑らかで、生態系は豊富です。

Golang and Python:違いを理解するGolang and Python:違いを理解するApr 18, 2025 am 12:21 AM

GolangとPythonの主な違いは、並行性モデル、タイプシステム、パフォーマンス、実行速度です。 1. GolangはCSPモデルを使用します。これは、同時タスクの高いタスクに適しています。 Pythonは、I/O集約型タスクに適したマルチスレッドとGILに依存しています。 2。Golangは静的なタイプで、Pythonは動的なタイプです。 3.ゴーランコンパイルされた言語実行速度は高速であり、Python解釈言語開発は高速です。

Golang vs. C:速度差の評価Golang vs. C:速度差の評価Apr 18, 2025 am 12:20 AM

Golangは通常Cよりも遅くなりますが、Golangはプログラミングと開発効率の同時により多くの利点があります。1)Golangのゴミ収集と並行性モデルにより、同時性の高いシナリオではうまく機能します。 2)Cは、手動のメモリ管理とハードウェアの最適化により、より高いパフォーマンスを取得しますが、開発の複雑さが高くなります。

Golang:クラウドコンピューティングとDevOpsのキー言語Golang:クラウドコンピューティングとDevOpsのキー言語Apr 18, 2025 am 12:18 AM

GolangはクラウドコンピューティングとDevOpsで広く使用されており、その利点はシンプルさ、効率性、および同時プログラミング機能にあります。 1)クラウドコンピューティングでは、GolangはGoroutineおよびチャネルメカニズムを介して同時リクエストを効率的に処理します。 2)DevOpsでは、Golangの高速コンピレーションとクロスプラットフォーム機能により、自動化ツールの最初の選択肢になります。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SecLists

SecLists

SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

PhpStorm Mac バージョン

PhpStorm Mac バージョン

最新(2018.2.1)のプロフェッショナル向けPHP統合開発ツール

AtomエディタMac版ダウンロード

AtomエディタMac版ダウンロード

最も人気のあるオープンソースエディター

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境