TCP サーバー、スレッド インターフェイスの作成
public class TCPServer implements Runnable {
private static final String TAG = "TCPServer";
private String chaSet = "UTF-8";
private int port;
private boolean isListen = true;
public TCPServer(int port) {
this.port = port;
}
@Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(port);
Log.d(TAG, "run:等待客户端连接... ");
//serverSocket.setSoTimeout(2000);
if (isListen) {
Socket socket = serverSocket.accept();
Log.d(TAG, "run: 客户端已连接");
if (socket != null) {
accept(socket);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void accept(Socket socket) {
InputStream is;
OutputStream os;
try {
is = socket.getInputStream();
os = socket.getOutputStream();
int len;
byte[] bytes = new byte[1024 * 4];
while (!socket.isClosed() && !socket.isInputShutdown()) {
while ((len = is.read(bytes)) != -1) {
Log.d(TAG, "accept: len: " + len);
byte[] content = new byte[bytes.length];
System.arraycopy(bytes, 0, content, 0, bytes.length);
String res = new String(content, chaSet);
String trim = res.trim(); //打印的时候去掉多余部分
if (onReceiveListener != null) {
onReceiveListener.receive(trim);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
//创建回调 用于在主线程中监听收消息事件
private onReceiveListener onReceiveListener;
public interface onReceiveListener {
void receive(String bytes);
}
public void setOnReceiveListener(onReceiveListener onReceiveListener) {
this.onReceiveListener = onReceiveListener;
}
}
メイン スレッドにスレッドを作成し、インターフェイス コールバック リスナーを呼び出します。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TCPServer tcpServer=new TCPServer(9899);
new Thread(tcpServer).start();
tcpServer.setOnReceiveListener(new TCPServer.onReceiveListener() {
@Override
public void receive(String bytes) {
Log.d(TAG, "receive: bytes: "+bytes);
}
});
}
}
次に、ネットワーク デバッグ アシスタントを使用して、サーバーにメッセージを送信します。
最後に、忘れずにネットワーク リクエストを申請してください。
<uses-permission android:name="android.permission.INTERNET"/>
新しい要件: データを受け入れるために必要なデータ構造を定義します。
定義されたデータ構造の長さ分析に従って、受信したバイトは次のように処理されます。
int postX = 0;
int postY = 0;
int size = 0;
int reserveOne = 0;
int reserveTwo = 0;
String reserve = "";
int color = 0;
long pngLen = 0L;
postX = getInt(bytes, 0, 4);
postY = getInt(bytes, 4, 4);
size = getInt(bytes, 8, 4);
reserveOne = getInt(bytes, 12, 4);
reserveTwo = getInt(bytes, 16, 4);
reserve = getString(bytes, 20, 56);
color = getInt(bytes, 76, 4);
pngLen = getInt(bytes, 80, 4);
if (onReceiveListener != null) {
onReceiveListener.receive(postX, postY, size, reserveOne, reserveTwo, reserve, color, pngLen);
//byte转int
public int getInt(byte[] srcBytes, int srcPos, int length) {
byte[] bytes = new byte[length];
System.arraycopy(srcBytes, srcPos, bytes, 0, length);
int anInt = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
return anInt;
}
//byte转String
public String getString(byte[] srcBytes, int srcPos, int length) {
byte[] bytes = new byte[length];
System.arraycopy(srcBytes, srcPos, bytes, 0, length);
String str = "解析错误";
try {
str = new String(bytes, 0, length, chaSet);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return str.trim();
}
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt()
このメソッドの機能は、指定されたバイト配列をbytes
整数値に変換することです。
まず、ByteBuffer.wrap(bytes)
新しいバイト バッファーが作成され、指定されたバイト配列にラップされますbytes
。これは、後続の操作がそのバッファに対して実行されることを意味します。
次に、.order(ByteOrder.LITTLE_ENDIAN)
バイトバッファのバイトオーダーをリトルエンディアンに設定します。バイト順序によって、マルチバイト データ型がメモリにどのように格納されるかが決まります。リトル エンディアンとは、下位バイトがメモリの下位アドレスに格納され、上位バイトがメモリ内の上位アドレスに格納されることを意味します。
最後に、.getInt()
バイト バッファから 4 バイトが読み取られ、整数値として解釈されます。バイト バッファの現在位置は、次の読み取り操作のために 4 バイトずつシフトされます。
ビッグエンディアンリトルエンディアンとは何ですか? chatGPT について質問されました
ビッグ エンディアンとリトル エンディアンは、マルチバイト データがメモリにどのように格納されるかを記述するために使用される 2 バイト オーダーです。
コンピュータでは、通常、マルチバイト データ型 (整数、浮動小数点数など) は複数のバイトで構成されます。これらのバイトは特定の順序でメモリに保存されます。ビッグエンディアンとリトルエンディアンは、これらのバイトがメモリ内でどのように配置されるかを指定します。
-
ビッグ エンディアン: ビッグ エンディアンでは、最上位バイト (Most Significant Byte、MSB) が最下位メモリ アドレスに格納され、最下位バイト (Least Significant Byte、LSB) が最上位メモリ アドレスに格納されます。数字の書き方と同様に、上位バイトが最初に来て、下位バイトが後に続きます。
-
リトル エンディアン: リトル エンディアンでは、最下位バイト (LSB) が最下位のメモリ アドレスに格納され、最上位バイト (MSB) が最上位のメモリ アドレスに格納されます。ビッグエンディアンとは逆に、下位バイトが最初に来て、上位バイトが続きます。
以下に、ビッグ エンディアンとリトル エンディアンの違いを示す例を示します。
4 バイトの整数値0x12345678
(16 進表現) を想定すると、次のようにメモリに格納されます。
-
ビッグエンディアン: アドレス上位 -> 低位
高地址 12 | 34 | 56 | 78 低地址
-
リトルエンディアン: アドレス下位->上位
低地址 78 | 56 | 34 | 12 高地址
ビッグ エンディアンとリトル エンディアンは、マルチバイト データ型のストレージが異なるだけであることに注意してください。シングルバイト データ型 (文字など) の場合、データ型は 1 バイトしかないため、エンディアンは影響しません。バイト順序の選択は、さまざまなアーキテクチャや通信プロトコルで重要な意味を持ちます。特にクロスプラットフォーム通信やネットワーク通信では、バイト順序を正しく処理することが重要です。