यह गाइड Android में Client-Server आर्किटेक्चर को TCP Sockets का उपयोग करके लागू करने का एक व्यापक अवलोकन प्रदान करता है। इसमें दोनों पक्षों के लिए पूर्ण, उत्पादन-तैयार कोड पैटर्न, उचित संसाधन प्रबंधन और आधुनिक Android सर्वोत्तम अभ्यास शामिल हैं।
आर्किटेक्चर सारांश
┌─────────────────┐ ┌─────────────────┐ │ SERVER │ │ CLIENT │ │ (Listener) │◄── TCP Port 8080 ──►│ (Initiator) │ │ │ │ │ │ ServerSocket │ │ Socket │ │ accept() │ │ connect() │ │ Input/Output │ │ Input/Output │ └─────────────────┘ └─────────────────┘
साझा कॉन्फ़िगरेशन
// दोनों ऐप्स के लिए स्थिरांक
public final class NetworkConfig {
public static final int DEFAULT_PORT = 8080;
public static final int CONNECT_TIMEOUT_MS = 10000;
public static final int READ_TIMEOUT_MS = 30000;
public static final String CHARSET = "UTF-8";
}
Server पक्ष कार्यान्वयन
ServerManifest.xml
<manifest ... package="com.example.serversocket">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application ...>
<activity android:name=".ServerActivity" android:exported="true">
<intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter>
</activity>
</application>
</manifest>
ServerActivity.java (आधुनिक, साफ संस्करण)
package com.example.serversocket;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ServerActivity extends AppCompatActivity {
private static final int PORT = NetworkConfig.DEFAULT_PORT;
private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
private ExecutorService executor = Executors.newSingleThreadExecutor();
private TextView tvIp, tvStatus, tvLog;
private EditText etMessage;
private Button btnSend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_server);
tvIp = findViewById(R.id.tvIP);
tvStatus = findViewById(R.id.tvConnectionStatus);
tvLog = findViewById(R.id.tvMessages);
etMessage = findViewById(R.id.etMessage);
btnSend = findViewById(R.id.btnSend);
displayLocalIp();
startListening();
btnSend.setOnClickListener(v -> sendMessage());
}
private void displayLocalIp() {
executor.execute(() -> {
try {
String ip = getLocalIpAddress();
runOnUiThread(() -> tvIp.setText("Server IP: " + ip));
} catch (UnknownHostException e) {
runOnUiThread(() -> tvIp.setText("IP अनुपलब्ध"));
}
});
}
private String getLocalIpAddress() throws UnknownHostException {
WifiManager wm = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
WifiInfo wi = wm.getConnectionInfo();
int ipInt = wi.getIpAddress();
if (ipInt == 0) throw new UnknownHostException("Wi-Fi कनेक्टेड नहीं");
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array();
return InetAddress.getByAddress(bytes).getHostAddress();
}
private void startListening() {
executor.execute(() -> {
try {
serverSocket = new ServerSocket(PORT);
serverSocket.setSoTimeout(0); // अनंत प्रतीक्षा
runOnUiThread(() -> appendLog("Server प्रारंभ हुआ पोर्ट " + PORT + " पर"));
while (!Thread.currentThread().isInterrupted()) {
try {
clientSocket = serverSocket.accept();
clientSocket.setSoTimeout(NetworkConfig.READ_TIMEOUT_MS);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(),
java.nio.charset.StandardCharsets.UTF_8));
runOnUiThread(() -> {
tvStatus.setText("कनेक्टेड: " + clientSocket.getInetAddress().getHostAddress());
appendLog("क्लाइंट कनेक्टेड: " + clientSocket.getInetAddress().getHostAddress());
});
handleClient();
} catch (IOException e) {
if (!Thread.currentThread().isInterrupted()) {
runOnUiThread(() -> appendLog("कनेक्शन त्रुटि: " + e.getMessage()));
}
} finally {
closeClientSocket();
}
}
} catch (IOException e) {
runOnUiThread(() -> appendLog("Server त्रुटि: " + e.getMessage()));
}
});
}
private void handleClient() {
executor.execute(() -> {
try {
String line;
while ((line = in.readLine()) != null) {
String finalLine = line;
runOnUiThread(() -> appendLog("Client: " + finalLine));
}
} catch (IOException e) {
runOnUiThread(() -> appendLog("क्लाइंट डिस्कनेक्ट: " + e.getMessage()));
} finally {
closeClientSocket();
runOnUiThread(() -> tvStatus.setText("कनेक्शन की प्रतीक्षा..."));
}
});
}
private void sendMessage() {
String msg = etMessage.getText().toString().trim();
if (msg.isEmpty() || out == null) return;
out.println(msg);
appendLog("Server: " + msg);
etMessage.setText("");
}
private void appendLog(String msg) {
tvLog.append(msg + "\n");
// ऑटो-स्क्रॉल के लिए ScrollView का उपयोग करें
}
private void closeClientSocket() {
try {
if (in != null) in.close();
if (out != null) out.close();
if (clientSocket != null) clientSocket.close();
} catch (IOException ignored) {}
clientSocket = null;
in = null;
out = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdownNow();
try {
if (serverSocket != null) serverSocket.close();
} catch (IOException ignored) {}
closeClientSocket();
}
}
Client पक्ष कार्यान्वयन
ClientManifest.xml
<manifest ... package="com.example.clientsocket">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application ...>
<activity android:name=".ClientActivity" android:exported="true">
<intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter>
</activity>
</application>
</manifest>
ClientActivity.java (आधुनिक, साफ संस्करण)
package com.example.clientsocket;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ClientActivity extends AppCompatActivity {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private ExecutorService executor = Executors.newSingleThreadExecutor();
private EditText etIp, etPort, etMessage;
private TextView tvLog;
private Button btnConnect, btnSend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
etIp = findViewById(R.id.etIP);
etPort = findViewById(R.id.etPort);
tvLog = findViewById(R.id.tvMessages);
etMessage = findViewById(R.id.etMessage);
btnConnect = findViewById(R.id.btnConnect);
btnSend = findViewById(R.id.btnSend);
btnConnect.setOnClickListener(v -> connect());
btnSend.setOnClickListener(v -> send());
}
private void connect() {
String ip = etIp.getText().toString().trim();
String portStr = etPort.getText().toString().trim();
if (ip.isEmpty()) { etIp.setError("IP आवश्यक"); return; }
int port = portStr.isEmpty() ? NetworkConfig.DEFAULT_PORT : Integer.parseInt(portStr);
appendLog("कनेक्ट हो रहा है " + ip + ":" + port + "...");
executor.execute(() -> {
try {
socket = new Socket();
socket.connect(new java.net.InetSocketAddress(ip, port), NetworkConfig.CONNECT_TIMEOUT_MS);
socket.setSoTimeout(NetworkConfig.READ_TIMEOUT_MS);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream(),
java.nio.charset.StandardCharsets.UTF_8));
runOnUiThread(() -> {
appendLog("Server से कनेक्टेड!");
btnConnect.setEnabled(false);
btnSend.setEnabled(true);
});
listenForMessages();
} catch (IOException e) {
runOnUiThread(() -> appendLog("कनेक्शन विफल: " + e.getMessage()));
}
});
}
private void listenForMessages() {
executor.execute(() -> {
try {
String line;
while ((line = in.readLine()) != null) {
String finalLine = line;
runOnUiThread(() -> appendLog("Server: " + finalLine));
}
} catch (IOException e) {
runOnUiThread(() -> appendLog("डिस्कनेक्ट: " + e.getMessage()));
} finally {
runOnUiThread(() -> {
btnConnect.setEnabled(true);
btnSend.setEnabled(false);
});
closeResources();
}
});
}
private void send() {
String msg = etMessage.getText().toString().trim();
if (msg.isEmpty() || out == null) return;
out.println(msg);
appendLog("Client: " + msg);
etMessage.setText("");
}
private void appendLog(String msg) {
tvLog.append(msg + "\n");
}
private void closeResources() {
try {
if (in != null) in.close();
if (out != null) out.close();
if (socket != null) socket.close();
} catch (IOException ignored) {}
socket = null; in = null; out = null;
}
@Override
protected void onDestroy() {
super.onDestroy();
executor.shutdownNow();
closeResources();
}
}
लेआउट XML (दोनों के लिए समान संरचना)
पिछले लेख (1005061523) के लेआउट देखें - RelativeLayout जिसमें IP/Port इनपुट (Client), स्टेटस TextView, मैसेज लॉग TextView, इनपुट EditText, Send Button शामिल हैं।
मजबूती के लिए मुख्य सुधार
| क्षेत्र | मूल | सुधारा गया |
|---|---|---|
| थ्रेडिंग | कच्चे Thread | ExecutorService (SingleThreadExecutor) |
| संसाधन सफाई | न्यूनतम | onDestroy() में व्यापक |
| टाइमआउट | कोई नहीं | कनेक्ट/रीड टाइमआउट सेट |
| एन्कोडिंग | प्लेटफ़ॉर्म डिफ़ॉल्ट | स्पष्ट UTF-8 |
| ऑटो-फ्लश | मैन्युअल | PrintWriter(..., true) |
| पुनः कनेक्शन | केवल Server | दोनों पक्ष पुनः कनेक्ट कर सकते हैं |
उत्पादन के लिए उन्नत विषय
- प्रोटोकॉल डिज़ाइन: लंबाई-पूर्वनिर्धारित फ्रेम, JSON, Protobuf, MessagePack
- सुरक्षा: TLS/SSL (
SSLSocketFactory), प्रमाणपत्र पिनिंग - खोज: मैन्युअल IP प्रविष्टि के बजाय mDNS/Bonjour/NSD
- जीवनचक्र: ViewModel + Kotlin Coroutines/Flow
- पृष्ठभूमि: Foreground Service + WorkManager भरोसेमंद कनेक्टिविटी के लिए
- परीक्षण: MockWebServer, स्थानीय सर्वर के लिए एमुलेटर पोर्ट फ़ॉरवर्डिंग
निष्कर्ष
यह Client-Server Socket कार्यान्वयन Android नेटवर्क प्रोग्रामिंग के लिए एक ठोस आधार प्रदान करता है। मुख्य पैटर्न: पृष्ठभूमि निष्पादन के लिए ExecutorService, संसाधन प्रबंधन के लिए try-with-resources, स्पष्ट एन्कोडिंग, टाइमआउट, और उचित जीवनचक्र सफाई। वास्तविक दुनिया के ऐप्स के लिए, रखरखाव और मजबूती के लिए OkHttp, Ktor, या gRPC जैसे उच्च-स्तरीय फ्रेमवर्क पर विचार करें।