آموزش Socket در اندروید با پیاده سازی یک مثال


برنامه نویسی سوکت در اندروید

تبادل داده با Socket در اندروید

Socket ها در شبکه پایانه هایی هستند که به کمک آنها دیوایس ها می توانند با یکدیگر ارتباط برقرار کنند. به عبارت دقیق تر با جفت نمودن IP  آدرس و port number  یک سوکت بوجود می آید. برای ایجاد ارتباط به دو نوع Socket نیاز خواهیم داشت. یکی کلاینت و دیگری سرور. در این مقاله به پیاده سازی مثالی می پردازیم که در آن میان یک برنامه اندرویدی و یک سرور جاوا ارتباط برقرار می کنیم. در این آموزش از سری مجموعه صفر تا صد اندروید، اساس برنامه های چت با Socket را بهتر می توان درک نمود.

در حالت پایه برنامه ها در شبکه از ارتباط بین بخش سرور و کلاینت و یک داده برای تبادل تشکیل می شوند. سیستمی که برنامه را اجرا می کند، درخواستی را برای سرور ارسال می نماید. خود این سیستم که برنامه را اجرا می کند کلاینت به شمار می رود. منظور از سرور کامپیوتری ست که به تعداد بالایی درخواست پاسخگوست.

سمت سرور

برای برقراری ارتباط با سرور از طریق اینترنت در جاوا (به کمک TCP/IP) نیاز به افزودن java.net.Socket در برنامه خود دارید.

قدم اول : ایجاد پروژه جدید

یک پروژه جدید در اندروید استودیو ایجاد کرده و فایل xml مربوط به activity_main را می نویسیم:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   android:layout_margin = "16dp"
   tools:context = ".MainActivity">
   <TextView
      android:id = "@+id/tvIP"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:textAppearance = "@style/Base.TextAppearance.AppCompat.Medium" />
   <TextView
      android:id = "@+id/tvPort"
      android:layout_width = "wrap_content"
      android:layout_height = "wrap_content"
      android:layout_below = "@+id/tvIP"
      android:textAppearance = "@style/Base.TextAppearance.AppCompat.Medium" />
   <TextView
      android:id = "@+id/tvConnectionStatus"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_below = "@+id/tvPort"
      android:textAppearance = "@style/Base.TextAppearance.AppCompat.Medium" />
   <TextView
      android:id = "@+id/tvMessages"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_above = "@+id/etMessage"
      android:layout_below = "@+id/tvConnectionStatus"
      android:inputType = "textMultiLine"
      android:textAppearance = "@style/Base.TextAppearance.AppCompat.Medium" />
   <EditText
      android:id = "@+id/etMessage"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_above = "@+id/btnSend"
      android:hint = "Enter Message"
      android:inputType = "text" />
   <Button
      android:id = "@+id/btnSend"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_alignParentBottom = "true"
      android:text = "SEND" />
</RelativeLayout>

قدم دوم : کامل کردن MainActivity.java

برای جلوگیری از بلوکه شدن ترد UI، قسمت های زمان بر این برنامه را داخل یک thread جداگانه پیاده می کنیم.

package com.server.myapplication.server;
import android.annotation.SuppressLint;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
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;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
   ServerSocket serverSocket;
   Thread Thread1 = null;
   TextView tvIP, tvPort;
   TextView tvMessages;
   EditText etMessage;
   Button btnSend;
   public static String SERVER_IP = "";
   public static final int SERVER_PORT = 8080;
   String message;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      tvIP = findViewById(R.id.tvIP);
      tvPort = findViewById(R.id.tvPort);
      tvMessages = findViewById(R.id.tvMessages);
      etMessage = findViewById(R.id.etMessage);
      btnSend = findViewById(R.id.btnSend);
      try {
         SERVER_IP = getLocalIpAddress();
      } catch (UnknownHostException e) {
         e.printStackTrace();
      }
      Thread1 = new Thread(new Thread1());
      Thread1.start();
      btnSend.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
            message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
               new Thread(new Thread3(message)).start();
            }
         }
      });
   }
   private String getLocalIpAddress() throws UnknownHostException {
      WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
      assert wifiManager ! = null;
      WifiInfo wifiInfo = wifiManager.getConnectionInfo();
      int ipInt = wifiInfo.getIpAddress();
      return InetAddress.getByAddress(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ipInt).array()).getHostAddress();
   }
   private PrintWriter output;
   private BufferedReader input;
   class Thread1 implements Runnable {
      @Override
      public void run() {
         Socket socket;
         try {
            serverSocket = new ServerSocket(SERVER_PORT);
            runOnUiThread(new Runnable() {
               @Override
               public void run() {
                  tvMessages.setText("Not connected");
                  tvIP.setText("IP: " + SERVER_IP);
                  tvPort.setText("Port: " + String.valueOf(SERVER_PORT));
               }
            });
            try {
               socket = serverSocket.accept();
               output = new PrintWriter(socket.getOutputStream());
               input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
               runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                     tvMessages.setText("Connected\n");
                  }
               });
               new Thread(new Thread2()).start();
            } catch (IOException e) {
               e.printStackTrace();
            }
         } catch (IOException e) {
            e.printStackTrace();
         }
      }
   }
   private class Thread2 implements Runnable {
      @Override
      public void run() {
         while (true) {
            try {
               final String message = input.readLine();
               if (message ! = null) {
                  runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                        tvMessages.append("client:" + message + "\n");
                     }
                  });
               } else {
                  Thread1 = new Thread(new Thread1());
                  Thread1.start();
                  return;
               }
            } catch (IOException e) {
               e.printStackTrace();
            }
         }  
      }
   }
   class Thread3 implements Runnable {
      private String message;
      Thread3(String message) {
         this.message = message;
      }
      @Override
      public void run() {
         output.write(message);
         output.flush();
         runOnUiThread(new Runnable() {
            @Override
            public void run() {
               tvMessages.append("server: " + message + "\n");
               etMessage.setText("");
            }
         });
      }
   }
}

قدم سوم : تکمیل کردن مانیفست برنامه

<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
   package = "com.example.myapplication">
   <uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" />
   <uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
   <uses-permission android:name = "android.permission.INTERNET"/>
   <application
      android:allowBackup = "true"
      android:icon = "@mipmap/ic_launcher"
      android:label = "@string/app_name"
      android:roundIcon = "@mipmap/ic_launcher_round"
      android:supportsRtl = "true"
      android:theme = "@style/AppTheme">
      <activity android:name = ".MainActivity"
         android:label = "Server">
         <intent-filter>
            <action android:name = "android.intent.action.MAIN" />
            <category android:name = "android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
   </application>
</manifest>

برنامه نویسی بخش کلاینت

قدم اول :

یک پروژه جدید در اندروید استودیو ایجاد کرده و فایل activity_main.xml را به صورت زیر می نویسیم:

<?xml version = "1.0" encoding = "utf-8"?>
<RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
   xmlns:tools = "http://schemas.android.com/tools"
   android:layout_width = "match_parent"
   android:layout_height = "match_parent"
   android:layout_margin = "16dp"
   tools:context = ".MainActivity">
   <EditText
      android:id = "@+id/etIP"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:hint = "IP-Address"
      android:inputType = "text" />
   <EditText
      android:id = "@+id/etPort"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_below = "@+id/etIP"
      android:hint = "Port No"
      android:inputType = "number" />
   <Button
      android:id = "@+id/btnConnect"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_below = "@+id/etPort"
      android:layout_gravity = "center"
      android:layout_marginTop = "16dp"
      android:text = "Connect To Server" />
   <TextView
      android:id = "@+id/tvMessages"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_above = "@+id/etMessage"
      android:layout_below = "@+id/btnConnect"
      android:inputType = "textMultiLine"
      android:textAppearance = "@style/Base.TextAppearance.AppCompat.Medium" />
   <EditText
      android:id = "@+id/etMessage"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_above = "@+id/btnSend"
      android:hint = "Enter Message"
      android:inputType = "text" />
   <Button
      android:id = "@+id/btnSend"
      android:layout_width = "match_parent"
      android:layout_height = "wrap_content"
      android:layout_alignParentBottom = "true"
      android:text = "SEND" />
</RelativeLayout>

قدم دوم : کامل کردن کدهای MainActivity.java

package com.client.myapplication.client;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

@SuppressLint("SetTextI18n")
public class MainActivity extends AppCompatActivity {
   Thread Thread1 = null;
   EditText etIP, etPort;
   TextView tvMessages;
   EditText etMessage;
   Button btnSend;
   String SERVER_IP;
   int SERVER_PORT;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      etIP = findViewById(R.id.etIP);
      etPort = findViewById(R.id.etPort);
      tvMessages = findViewById(R.id.tvMessages);
      etMessage = findViewById(R.id.etMessage);
      btnSend = findViewById(R.id.btnSend);
      Button btnConnect = findViewById(R.id.btnConnect);
      btnConnect.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
            tvMessages.setText("");
            SERVER_IP = etIP.getText().toString().trim();
            SERVER_PORT = Integer.parseInt(etPort.getText().toString().trim());
            Thread1 = new Thread(new Thread1());
            Thread1.start();
         }
      });
      btnSend.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
            String message = etMessage.getText().toString().trim();
            if (!message.isEmpty()) {
               new Thread(new Thread3(message)).start();
            }
         }
      });
   }
   private PrintWriter output;
   private BufferedReader input;
   class Thread1 implements Runnable {
      public void run() {
         Socket socket;
         try {
            socket = new Socket(SERVER_IP, SERVER_PORT);
            output = new PrintWriter(socket.getOutputStream());
            input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            runOnUiThread(new Runnable() {
               @Override
               public void run() {
                  tvMessages.setText("Connected\n");
               }
            });
            new Thread(new Thread2()).start();
         } catch (IOException e) {
         e.printStackTrace();
         }
      }
   }
   class Thread2 implements Runnable {
      @Override
      public void run() {
         while (true) {
            try {
               final String message = input.readLine();
               if (message ! = null) {
                  runOnUiThread(new Runnable() {
                     @Override
                     public void run() {
                        tvMessages.append("server: " + message + "\n");
                     }
                  });
               } else {
                  Thread1 = new Thread(new Thread1());
                  Thread1.start();
                  return;
               }
            } catch (IOException e) {
               e.printStackTrace();
            }
         }
      }
   }
   class Thread3 implements Runnable {
      private String message;
      Thread3(String message) {
         this.message = message;
      }
      @Override
      public void run() {
         output.write(message);
         output.flush();
         runOnUiThread(new Runnable() {  
            @Override
            public void run() {
               tvMessages.append("client: " + message + "\n");
               etMessage.setText("");
            }
         });
      }
   }
}

قدم سوم : تکمیل نمودن اندروید مانیفست

<?xml version = "1.0" encoding = "utf-8"?>
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
   package = "com.client.myapplication.client">
   <uses-permission android:name = "android.permission.INTERNET" />
   <uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
   <application
      android:allowBackup = "true"
      android:icon = "@mipmap/ic_launcher"
      android:label = "@string/app_name"
      android:roundIcon = "@mipmap/ic_launcher_round"
      android:supportsRtl = "true"
      android:theme = "@style/AppTheme">
      <activity
         android:name = "com.client.myapplication.client.MainActivity"
         android:label = "Client">
         <intent-filter>
            <action android:name = "android.intent.action.MAIN" />
            <category android:name = "android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
   </application>
</manifest>

اجرای برنامه :

هر دو برنامه سرور و کلاینت را اجرا می کنیم. نتیجه در برنامه سمت سرور به این شکل خواهد بود :

برنامه نویسی سوکت در اندروید

و در سمت کلاینت :

برنامه نویسی سوکت در اندروید

نتیجه اتصال یا عدم اتصال در برنامه سمت سرور مشخص می گردد :

برنامه نویسی سوکت در اندروید

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز / 5. تعداد:

2 دیدگاه در نوشته: “آموزش Socket در اندروید با پیاده سازی یک مثال

  1. رحمانیان گفت:

    با عرض سلام خانم نژند. من رحمانیان هستم استاد دانشگاه جهرم. این اپ را می توان بدون تعارف بهترین اپ ارائه شده توسط وبلاگهای فارسی زبان دانست. منتهی مشکلی که دارد کند بودن آن است که نمی دونم علت واقعی آن چیست. برای برنامه سرور من از این برنامه استفاد نکرده ام و سرور من در واقع یک میکروکنترلر است. اما جهت برنامه کلاینت از همین برنامه استفاده کرده ام. مشکل اینجاست که زمانی که پیامی ارسال می کنم با تاخیر به سرور می رسد. شما برای این مشکل پیشنهادی دارید؟ با سپاس

    1. نسیم نژند گفت:

      با سلام و سپاس از شما استاد گرامی که این مقاله را انتخاب نمودید.

      جسارتا در این کتابخانه https://github.com/samigehi/AsyncSocket استفاده از تردها و عملیات بافری هم سمت سرور و هم سمت کلاینت بهینه شده است. به کمک کلاسی که از AsyncTask ارث بری کند نیز می توان دریافت و ارسال پیام را به شکلی مدیریت شده در برنامه پیش برد.
      با سپاس مجدد از شما

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

Enter Captcha Here : *

Reload Image