背景
MateBook Ubuntu18.04
MacBook MacOS HighSierra 10.13.6
2台のコンピュータでファイル転送が必要になる場合があります。TCPプロトコルでソケットプログラミングを選択するの
は非常に簡単です。 UDPパフォーマンス。今すぐ実装して、当時見過ごされていたいくつかの知識ポイントを確認してみましょう
基礎知識
ネットワーク通信要素:プロトコル、IP、ポート
TCPプロトコル
ネットワークの2種類のスイッチ操作:回線交換、パケット交換
- 回線断絶:リンク確立、データ送信、リンク切断を含む固定リンク[電話システム]
- パケット交換:データはパケットで送信され、リンクはルート[ネットワーク]を介して管理されます
ネットワークが提供できる2つのタイプのサービス:コネクションレス型とコネクション型
- コネクションレス:コネクションレスサービス、ネットワークはすべてのデータパケットを送信するために最善を尽くします。損失、シーケンシャル、信頼性の低い接続、高速配信、UDPプロトコルの保証はありません。
- コネクション型:コネクション型のサービス。送受信の両端で通信を行い、コールを受信し、TCPプロトコルを使用します。
プロトコルは信頼性の高いTCP / IP プロトコルを選択し、複数のハンドシェイク、接続指向で接続を確立します
IPアドレス
ネットワーク内のデバイス端末の一意の識別子
- LAN、ローカルエリアネットワーク、一般的に使用され、通常は単一の管理ドメインの下。LAN間のインターネット接続
- サブネットはルーター、ルーターを介して相互に接続されています。ルーティングとは、複数のサブネットにわたる送信元から宛先へのパスの選択を指します
- 4種類のIPアドレス、192.168.1.1はタイプCの予約済みIPであり、ルーターの設定に使用されます。192.168.xxは通常ローカルエリアネットワークまたは内部ネットワークであり、接続された各デバイスには内部ネットワークIPが割り当てられます
- イントラネットIPは、イントラネット間の伝送に使用できます。すべてのイントラネットデバイスには、オペレーターが割り当てたゲートウェイIPである共通の外部IPがあります。外部ネットワークIPは、外部ネットワークデバイス間の通信に使用されます。私の基本的な2台のコンピューターは、同じローカルエリアネットワークにあることが保証されています。イントラネットIPを使用するだけです。
- IPアドレスの割り当ては固定されておらず、動的に割り当てられる可能性が高いため、各リンク通信中に2台のコンピューターの現在のIPアドレスを確認する必要があります
ポート
プログラムはIP + PORTを介して通信し、
オペレーティングシステムコールを使用してポートにバインドします。システムコールはポートを開きます。
パケットには送信元ポートと宛先ポートがあり、パケットはリンクの両端のポート間で確立されます
。2つの端末は異なるポートを使用でき、2者はそれを行うことを決定できます。
ポートの実行時に有効にできるサービスは1つだけです。それ以外の場合、ポートは占有されています
Java実装
サーバー【受信】
- ServerSocketオブジェクトを作成し、通信ポートを使用します
- ServerSocketオブジェクトをリッスンして接続を確立した後、Socketオブジェクトを取得します
- チャネルのデータを読み取る入力ストリームオブジェクトを作成します[バイトストリーム]
- 出力ストリームオブジェクトを作成し、チャネルのデータをファイルに書き込みます
- 送信が完了したら、出力ストリームオブジェクトを作成し、フィードバックをクライアントに送信します
- ストリームオブジェクトリソースを閉じる
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
//This class serves as the Socket Sever accepting files from MacBook
public class TCPServer {
public static void main(String[] args) throws IOException {
//create server socket instance, listening to local port 8888,specified for client-server connection
ServerSocket ss = new ServerSocket(8888);
//create socket instance from method accept, blocked method until binding occurs
Socket s = ss.accept();
//create byte input stream from socket
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
//create byte output stream to local destination, specify file name and directory for every transfer
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("/home/aries/Downloads/1.jpg"));
//read data from socket stream and write to file stream
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
//notify for successful transfer
OutputStream os = s.getOutputStream();
os.write("MateBook已接收".getBytes());
//resource release
bos.close();
//release server socket if necessary
s.close();
}
}
クライアント【送信】
- Socketオブジェクトを作成してサーバーに接続する
- 入力ストリームオブジェクトを作成し、ファイルからデータを読み取ります[バイトストリーム]
- 出力ストリームオブジェクトを作成し、ソケットチャネルに書き込みます
- 転送を完了するようチャネルに通知する
- サーバーのフィードバックを受け取る
- ストリームオブジェクトリソースを閉じ、クライアントを閉じます
import java.io.*;
import java.net.Socket;
//This class serves as the Client socket sending files to MacBook
public class TCPClient {
public static void main(String[] args) throws IOException {
//create client socket instance by hostIP and hostPort
Socket s = new Socket("192.168.1.131", 8888);
//create byte input stream instance by file input stream, specify the directory and file name each time sending to Mac
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/home/aries/Documents/Test.txt"));
//create byte output stream instance by client socket
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
//read data from file stream and write to socket stream
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
//Disables the output stream for this socket
s.shutdownOutput();
//accept feedback from server
InputStream is = s.getInputStream();
byte[] feedback = new byte[1024];
int length = is.read(feedback);
String server_ack = new String(feedback, 0, length);
System.out.println(server_ack);
bis.close();
s.close();
}
}
考えて注意を払う
サーバーはクライアントを監視し、クライアントの実行時にリンクを確立するため、サーバーはクライアントよりも早く起動する必要があります。
サーバーがパブリックネットワーク上にあり、イントラネット上のさまざまなデバイスがメッセージをパブリックネットワークに送信するとします。このとき、内部ネットワークデバイスの外部ネットワークIPは同じですが、内部ネットワークIPが異なります。サーバーとの接続は、パブリックIPによって実現されます。このとき、パブリックネットワークサーバーでは、2つの異なる物理デバイスが同じものと同じデータを送信します。ただし、マルチスレッドを使用して、送信待機の問題を回避できます。
クライアントはパブリックネットワーク上にあり、内部ネットワークにはメッセージを受信するためのさまざまなサーバーデバイスがあると想定されています。現時点では、サーバーのパブリックIPは同じですが、内部IPは異なります。一方のサーバーの電源が入っている場合、もう一方のサーバーの電源が入っているときにエラーが報告されると思います。私はエラーを試していませんが、ポートが誤って占有されているか、サーバーの電源が入っている可能性があります。上記と同じ考え方なので、2つのイントラネットサーバーをまとめて考える必要があり、ポートが開かれると、ポートが繰り返し開かれると、エラーが報告されます。2つのサーバーに対して異なるポートを開くことができますが、これは実際のロジックと一致していません。クライアントの場合、複数のサーバーがあることを認識していない可能性があるためです。したがって、リバースプロキシを使用して、同じIP内の複数のサーバーを管理して分散を実現できます。サーバーが異なるLANにある場合は、サーバークラスターを管理するための分散管理アーキテクチャも必要です。
プログラムは比較的シンプルですが、多くの知識が忘れられているので、考えてみると面白いでしょう。要約すると、すべてを配布できるということです。このアイデアは、ネットワークプログラミングではまだ非常に一般的です。