ホームページ バックエンド開発 Python チュートリアル ソケットプログラミングの練習

ソケットプログラミングの練習

Nov 05, 2016 am 09:58 AM
socket

ソケットとは英語で「溝(二つの物をつなぐ)」を意味し、「目の窩」を意味するアイソケットと同じく「ソケット」という意味もあります。コンピューター サイエンスでは、ソケットは通常、接続の 2 つのエンドポイントを指します。ここでの接続は、UNIX ドメイン ソケットのように同じマシン上に存在する場合もあれば、ネットワーク ソケットのように異なるマシン上に存在する場合もあります。

この記事では、ネットワーク モデルにおける位置、API プログラミング パラダイム、一般的なエラーなど、最も一般的に使用されるネットワーク ソケットに焦点を当て、最後に Python 言語のソケット API を使用していくつかの実用的な例を実装します。ソケットは中国語では一般に「ソケット」と訳されますが、これは「忠実でエレガントな」訳を期待していなかったので、この記事では英語の表現をそのまま使用しています。この記事のすべてのコードは、socket.py リポジトリにあります。

概要

ソケットは、一般的な技術仕様として、1983 年にバークレー大学によって 4.2BSD Unix 用に初めて提供され、その後徐々に POSIX 標準に進化しました。ソケット API は、アプリケーションがソケット テクノロジの使用を制御できるようにするオペレーティング システムによって提供されるプログラミング インターフェイスです。 Unix の哲学には、すべてがファイルであるという原則があるため、ソケットとファイルの API の使用法は非常に似ており、読み取り、書き込み、開く、閉じるなどの操作を実行できます。

現在のネットワーク システムは階層化されており、理論的には OSI モデル、業界では TCP/IP プロトコル スイートが使用されます。比較は次のとおりです:

ソケットプログラミングの練習

各層には対応するプロトコルがあります。ソケット API は、ネットワーク プログラミングのためにオペレーティング システムによって提供される単なるインターフェイスであり、アプリケーション間で動作します。平

ソケットプログラミングの練習

通常、Web サイトで電子メールを送信するために使用される HTTP プロトコルは、Socket API に基づいて構築されています。

ソケットには 2 つの必要なコンポーネントが含まれています:

アドレス。192.168.0.1:80 のように、IP とポートで構成されます。

プロトコル、ソケットで使用される送信プロトコルには、現在 TCP、UDP、Raw IP の 3 種類があります。

アドレスとプロトコルによってソケットが決定されます。1 つのマシン上に存在できる同一のソケットは 1 つだけです。 TCP ポート 53 のソケットと UDP ポート 53 のソケットは 2 つの異なるソケットです。

ソケットがデータを送信するさまざまな方法 (使用されるさまざまなプロトコル) に応じて、ソケットは次の 3 つのタイプに分類できます:

「接続指向」ソケットとしても知られるストリーム ソケットは、TCP プロトコルを使用します。実際の通信には接続が必要であり、送信されるデータは特定の構造を持たないため、上位プロトコルでデータセパレーター自体を定義する必要がありますが、データが信頼できるという利点があります。

「コネクションレス」ソケットとも呼ばれるデータグラム ソケットは、UDP プロトコルを使用します。実際の通信の前に接続する必要はありません。利点の 1 つは、UDP データ パケットが自己区切りであることです。つまり、各データ パケットがデータの始まりと終わりをマークします。欠点は、データの信頼性が低いことです。

生のソケットは通常、ルーターやその他のネットワーク機器で使用されます。この種のソケットは、TCP/IP プロトコル スイートのトランスポート層を経由せず、インターネット層からアプリケーション層に直接接続されます。現時点では、tcp または udp ヘッダー情報は含まれません。

ソケットプログラミングの練習

PythonソケットAPI

Pythonは、ソケットのアドレス属性を表すために(ip, port)のタプルを使用し、プロトコルタイプを表すためにAF_*を使用します。

データ通信には、send/recv または read/write の 2 つの動詞セットから選択できます。読み取り/書き込みメソッドは Java でも使用されます。このメソッドについてはここではあまり説明しませんが、次の点に注意してください:

読み取り/書き込みはバッファーのある「ファイル」に対して動作するため、読み取りと書き込みの後、実際にデータを送信または読み取るには、flush メソッドを呼び出す必要があります。そうしないと、データはバッファ内に残ります。

TCPソケット

TCPソケットは接続する前に接続を確立する必要があるため、そのモードはUDPソケットよりも責任があります。詳細は次のとおりです。

ソケットプログラミングの練習

各 API の具体的な意味についてはここでは説明しません。Python 言語で実装された echo サーバーを参照してください。

# echo_server.py 
# coding=utf8 
import socket 
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
# 设置 SO_REUSEADDR 后,可以立即使用 TIME_WAIT 状态的 socket 
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind(('', 5500)) 
sock.listen(5)
ログイン後にコピー
def handler(client_sock, addr): 
    print('new client from %s:%s' % addr) 
    msg = client_sock.recv(1024) 
    client_sock.send(msg) 
    client_sock.close() 
    print('client[%s:%s] socket closed' % addr) 
 
if __name__ == '__main__': 
    while 1: 
        client_sock, addr = sock.accept() 
        handler(client_sock, addr)
ログイン後にコピー
# echo_client.py 
# coding=utf8 
import socket 
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.connect(('', 5500)) 
sock.send('hello socket world') 
print sock.recv(1024)
ログイン後にコピー

上記の単純なエコー サーバー コードで注意すべき点は、TIME_WAIT 状態のソケットがすぐに使用できるように、サーバー側のソケットが SO_REUSEADDR に設定されていることです。状態は後ほど図を変更する際に詳しく紹介します。

UDPソケット🎜

ソケットプログラミングの練習

UDP socket server 端代码在进行bind后,无需调用listen方法。

# udp_echo_server.py 
# coding=utf8 
import socket 
 
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
# 设置 SO_REUSEADDR 后,可以立即使用 TIME_WAIT 状态的 socket 
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind(('', 5500)) 
# 没有调用 listen 
 
if __name__ == '__main__': 
    while 1: 
        data, addr = sock.recvfrom(1024) 
 
        print('new client from %s:%s' % addr) 
        sock.sendto(data, addr) 
 
# udp_echo_client.py 
# coding=utf8 
import socket 
 
udp_server_addr = ('', 5500) 
 
if __name__ == '__main__': 
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
    data_to_sent = 'hello udp socket' 
    try: 
        sent = sock.sendto(data_to_sent, udp_server_addr) 
        data, server = sock.recvfrom(1024) 
        print('receive data:[%s] from %s:%s' % ((data,) + server)) 
    finally: 
        sock.close()
ログイン後にコピー

常见陷阱

忽略返回值

本文中的 echo server 示例因为篇幅限制,也忽略了返回值。网络通信是个非常复杂的问题,通常无法保障通信双方的网络状态,很有可能在发送/接收数据时失败或部分失败。所以有必要对发送/接收函数的返回值进行检查。本文中的 tcp echo client 发送数据时,正确写法应该如下:

total_send = 0 
content_length = len(data_to_sent) 
while total_send < content_length: 
    sent = sock.send(data_to_sent[total_send:]) 
    if sent == 0: 
        raise RuntimeError("socket connection broken") 
    total_send += total_send + sent
ログイン後にコピー

send/recv操作的是网络缓冲区的数据,它们不必处理传入的所有数据。

一般来说,当网络缓冲区填满时,send函数就返回了;当网络缓冲区被清空时,recv 函数就返回。

当 recv 函数返回0时,意味着对端已经关闭。

可以通过下面的方式设置缓冲区大小。

s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, buffer_size)
ログイン後にコピー

认为 TCP 具有 framing

TCP 不提供 framing,这使得其很适合于传输数据流。这是其与 UDP 的重要区别之一。UDP 是一个面向消息的协议,能保持一条消息在发送者与接受者之间的完备性。

ソケットプログラミングの練習

代码示例参考:framing_assumptions

TCP 的状态机

在前面echo server 的示例中,提到了TIME_WAIT状态,为了正式介绍其概念,需要了解下 TCP 从生成到结束的状态机器。(图片来源)

ソケットプログラミングの練習

这个状图转移图非常非常关键,也比较复杂,我自己为了方便记忆,对这个图进行了拆解,仔细分析这个图,可以得出这样一个结论,连接的打开与关闭都有被动(passive)与主动(active)两种,主动关闭时,涉及到的状态转移最多,包括FIN_WAIT_1、FIN_WAIT_2、CLOSING、TIME_WAIT。

此外,由于 TCP 是可靠的传输协议,所以每次发送一个数据包后,都需要得到对方的确认(ACK),有了上面这两个知识后,再来看下面的图:

ソケットプログラミングの練習

在主动关闭连接的 socket 调用 close方法的同时,会向被动关闭端发送一个 FIN

对端收到FIN后,会向主动关闭端发送ACK进行确认,这时被动关闭端处于 CLOSE_WAIT 状态

当被动关闭端调用close方法进行关闭的同时向主动关闭端发送 FIN 信号,接收到 FIN 的主动关闭端这时就处于 TIME_WAIT 状态

这时主动关闭端不会立刻转为 CLOSED 状态,而是需要等待 2MSL(max segment life,一个数据包在网络传输中最大的生命周期),以确保被动关闭端能够收到最后发出的 ACK。如果被动关闭端没有收到最后的 ACK,那么被动关闭端就会重新发送 FIN,所以处于TIME_WAIT的主动关闭端会再次发送一个 ACK 信号,这么一来(FIN来)一回(ACK),正好是两个 MSL 的时间。如果等待的时间小于 2MSL,那么新的socket就可以收到之前连接的数据。

前面 echo server 的示例也说明了,处于 TIME_WAIT 并不是说一定不能使用,可以通过设置 socket 的 SO_REUSEADDR 属性以达到不用等待 2MSL 的时间就可以复用socket 的目的,当然,这仅仅适用于测试环境,正常情况下不要修改这个属性。

实战

HTTP UA

http 协议是如今万维网的基石,可以通过 socket API 来简单模拟一个浏览器(UA)是如何解析 HTTP 协议数据的。

#coding=utf8 
import socket 
 
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
baidu_ip = socket.gethostbyname(&#39;baidu.com&#39;) 
sock.connect((baidu_ip, 80)) 
print(&#39;connected to %s&#39; % baidu_ip) 
 
req_msg = [ 
    &#39;GET / HTTP/1.1&#39;, 
    &#39;User-Agent: curl/7.37.1&#39;, 
    &#39;Host: baidu.com&#39;, 
    &#39;Accept: */*&#39;, 
] 
delimiter = &#39;\r\n&#39; 
 
sock.send(delimiter.join(req_msg)) 
sock.send(delimiter) 
sock.send(delimiter) 
 
print(&#39;%sreceived%s&#39; % (&#39;-&#39;*20, &#39;-&#39;*20)) 
http_response = sock.recv(4096) 
print(http_response)
ログイン後にコピー

运行上面的代码可以得到下面的输出

--------------------received-------------------- 
HTTP/1.1 200 OK 
Date: Tue, 01 Nov 2016 12:16:53 GMT 
Server: Apache 
Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT 
ETag: "51-47cf7e6ee8400" 
Accept-Ranges: bytes 
Content-Length: 81 
Cache-Control: max-age=86400 
Expires: Wed, 02 Nov 2016 12:16:53 GMT 
Connection: Keep-Alive 
Content-Type: text/html 
 
<html> 
<meta http-equiv="refresh" content="0;url=http://www.baidu.com/"> 
</html>
ログイン後にコピー

http_response是通过直接调用recv(4096)得到的,万一真正的返回大于这个值怎么办?我们前面知道了 TCP 协议是面向流的,它本身并不关心消息的内容,需要应用程序自己去界定消息的边界,对于应用层的 HTTP 协议来说,有几种情况,最简单的一种时通过解析返回值头部的Content-Length属性,这样就知道body的大小了,对于 HTTP 1.1版本,支持Transfer-Encoding: chunked传输,对于这种格式,这里不在展开讲解,大家只需要知道, TCP 协议本身无法区分消息体就可以了。对这块感兴趣的可以查看 CPython 核心模块 http.client

Unix_domain_socket

UDS 用于同一机器上不同进程通信的一种机制,其API适用与 network socket 很类似。只是其连接地址为本地文件而已。

代码示例参考:uds_server.py、uds_client.py

ping

ping 命令作为检测网络联通性最常用的工具,其适用的传输协议既不是TCP,也不是 UDP,而是 ICMP,利用 raw sockets,我们可以适用纯 Python 代码来实现其功能。

代码示例参考:ping.py

netstat vs ss

netstat 与 ss 是类 Unix 系统上查看 Socket 信息的命令。netstat 是比较老牌的命令,我常用的选择有

-t,只显示 tcp 连接

-u,只显示 udp 连接

-n,不用解析hostname,用 IP 显示主机,可以加快执行速度

-p,查看连接的进程信息

-l,只显示监听的连接

ss 是新兴的命令,其选项和 netstat 差不多,主要区别是能够进行过滤(通过state与exclude关键字)。

$ ss -o state time-wait -n | head 
Recv-Q Send-Q             Local Address:Port               Peer Address:Port 
0      0                 10.200.181.220:2222              10.200.180.28:12865  timer:(timewait,33sec,0) 
0      0                      127.0.0.1:45977                 127.0.0.1:3306   timer:(timewait,46sec,0) 
0      0                      127.0.0.1:45945                 127.0.0.1:3306   timer:(timewait,6.621ms,0) 
0      0                 10.200.181.220:2222              10.200.180.28:12280  timer:(timewait,12sec,0) 
0      0                 10.200.181.220:2222              10.200.180.28:35045  timer:(timewait,43sec,0) 
0      0                 10.200.181.220:2222              10.200.180.28:42675  timer:(timewait,46sec,0) 
0      0                      127.0.0.1:45949                 127.0.0.1:3306   timer:(timewait,11sec,0) 
0      0                      127.0.0.1:45954                 127.0.0.1:3306   timer:(timewait,21sec,0) 
0      0               ::ffff:127.0.0.1:3306           ::ffff:127.0.0.1:45964  timer:(timewait,31sec,0)
ログイン後にコピー

这两个命令更多用法可以参考:

SS Utility: Quick Intro

10 basic examples of linux netstat command

总结

我们的生活已经离不开网络,平时的开发也充斥着各种复杂的网络应用,从最基本的数据库,到各种分布式系统,不论其应用层怎么复杂,其底层传输数据的的协议簇是一致的。Socket 这一概念我们很少直接与其打交道,但是当我们的系统出现问题时,往往是对底层的协议认识不足造成的,希望这篇文章能对大家编程网络方面的程序有所帮助。


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホット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

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

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

PHP+SocketシリーズのIO多重化とWebサーバーの実装 PHP+SocketシリーズのIO多重化とWebサーバーの実装 Feb 02, 2023 pm 01:43 PM

この記事では、php+socket に関する関連知識を提供します。主に IO 多重化と php+socket が Web サーバーを実装する方法を紹介します。興味のある方は以下をご覧ください。皆様のお役に立てれば幸いです。

Pythonのsocketとsocketserverの使い方 Pythonのsocketとsocketserverの使い方 May 28, 2023 pm 08:10 PM

1. TCP プロトコルに基づくソケット プログラミング 1. ソケット ワークフローはサーバー側から始まります。サーバーはまずソケットを初期化し、次にポートにバインドし、ポートをリッスンし、accept を呼び出してブロックし、クライアントが接続するのを待ちます。このとき、クライアントがSocketを初期化してからサーバーに接続(connect)すると、接続に成功するとクライアントとサーバー間の接続が確立されます。クライアントがデータ リクエストを送信し、サーバーがリクエストを受信して​​処理し、次に応答データをクライアントに送信し、クライアントがデータを読み取り、最後に接続を閉じます。インタラクションは終了します。インタラクションを実装するには、次の Python コードを使用します。 :インポートソ

Spring Boot + Vue を使用してソケット通知プッシュを実装する方法 Spring Boot + Vue を使用してソケット通知プッシュを実装する方法 May 27, 2023 am 08:47 AM

SpringBoot 側の最初のステップは依存関係を導入することです。まず、WebSocket に必要な依存関係と、出力形式 com.alibabafastjson1.2.73org.springframework.bootspring-boot-starter-websocket を処理するための依存関係を導入する必要があります。 2 番目のステップは、WebSocket 構成クラス importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Config を作成することです。

PHPソケットが接続できない場合の対処法 PHPソケットが接続できない場合の対処法 Nov 09, 2022 am 10:34 AM

php ソケットに接続できない問題の解決策: 1. php でソケット拡張機能が有効になっているかどうかを確認します; 2. php.ini ファイルを開いて、「php_sockets.dll」が読み込まれているかどうかを確認します; 3. 「php_sockets.dll」のコメントを解除します」。

C# における一般的なネットワーク通信とセキュリティの問題と解決策 C# における一般的なネットワーク通信とセキュリティの問題と解決策 Oct 09, 2023 pm 09:21 PM

C# におけるネットワーク通信とセキュリティの一般的な問題と解決策 今日のインターネット時代では、ネットワーク通信はソフトウェア開発に不可欠な部分となっています。 C# では通常、データ送信のセキュリティ、ネットワーク接続の安定性など、ネットワーク通信の問題が発生します。この記事では、C# における一般的なネットワーク通信とセキュリティの問題について詳しく説明し、対応する解決策とコード例を提供します。 1. ネットワーク通信の問題 ネットワーク接続の中断: ネットワーク通信プロセス中に、ネットワーク接続が中断される場合があります。

PHPとSocketを用いたリアルタイムファイル転送技術の研究 PHPとSocketを用いたリアルタイムファイル転送技術の研究 Jun 28, 2023 am 09:11 AM

インターネットの発展に伴い、ファイル転送は人々の日常の仕事や娯楽に不可欠な部分になりました。ただし、電子メールの添付ファイルやファイル共有 Web サイトなどの従来のファイル転送方法には一定の制限があり、リアルタイム性とセキュリティのニーズを満たすことができません。したがって、PHP と Socket テクノロジを使用してリアルタイムのファイル転送を実現することが新しいソリューションになりました。この記事では、PHP と Socket テクノロジを使用してリアルタイム ファイル転送を実現する技術原理、利点、アプリケーション シナリオを紹介し、具体的なケースを通じてこのテクノロジの実装方法を示します。テクノロジー

PHP+Socketシリーズはクライアントとサーバー間のデータ転送を実現します PHP+Socketシリーズはクライアントとサーバー間のデータ転送を実現します Feb 02, 2023 am 11:35 AM

この記事では、php+socket に関する関連知識を提供します。 php+socket はどのようにしてクライアントとサーバー間のデータ通信を実現するのでしょうか?興味のある方は以下をご覧ください。皆様のお役に立てれば幸いです。

WebSocketチャットルームを実現するPHP+Socketシリーズ WebSocketチャットルームを実現するPHP+Socketシリーズ Feb 02, 2023 pm 04:39 PM

この記事では、php+socket に関する関連知識を提供し、主に php ネイティブ ソケットを使用して簡単な Web チャット ルームを実装する方法を紹介します。興味のある方は以下をご覧ください。皆様のお役に立てれば幸いです。

See all articles