آموزش ارتباط کلاینت با سرور از طریق سوکت
یکی از دوستان یک سوال در مورد ارتباط با سرور از طریق سوکت مطرح کرده بود. در این تاپیک یک توضیح در مورد سوکت پروگرمینگ می دهم. امیدوارم بتونه به کسانی که نیاز دارند کمک کنه.
سوکت درواقع یک کانکشن هست. روشش هم این هست که شما سمت سرور یک Lisener می نویسید برای گوش دادن به بیرون و سمت کلاینت هم یک کلاینت از سوکت تعریف می کنید برای متصل شدن به سرور.
من در این پست هم کد مربوط به سمت سرور و هم کلاینت را به زبان جاوا ( که طبیعتا قابل استفاده در اندروید است) می نویسم ، اما اصول این کار در تمام زبان ها یکی است و شاید فقط چند خط جزئی فرق کند.
شما از این کلاس ها می توانید در برنامه های اندرویدی خود برای چت ، بازی های آنلاین چند نفره تحت شبکه داخلی یا تحت سرور ، یا دریافت Push Notification های اختصاصی خودتان به خوبی استفاده کنید. البته گفتم که سمت سرور را نیز با java نوشتم (که برای چت یا بازی های آنلاین درون شبکه محلی نیازی به تغییر آنها ندارید) ولی برای سمت سرور شما می توانید دستور معادل آن را در تمام زبانهای دیگر به راحتی پیدا کنید.
تمام کلاس ها دارای interface برای ایجاد ارتباط هستند. بدین شکل هر جای برنامه تان که یک شیء از آنها تعریف کردید می توانید listener های مربوط به متصل شدن ، قطع شدن ارتباط و دریافت اطلاعات را مدیریت کنید.
لطفا :
- سوالات خود را زیر هر پست به صورت نظر ارسال کنید و پست نزنید.
- اگر سوالی پرسیدید منتظر باشید تا جواب دهم. ممکن است هر روز فرصت نکنم به تاپیک سر بزنم.
- ترجیحا به جای نوشتن تشکر از دکمه لایک استفاده کنید (چون فکر کنم اونقدر نظرات زیاد بشوند که کسی حوصله نکند همه را بخواند)
سمت کلاینت
ابتدا سمت کلاینت را توضیح می دهم چون کمی ساده تر است.
خلاصه ی مطلب این است:
1- یک سوکت تعریف کنید:
Socket socketToServer = new Socket();
2- سوکت را متصل کنید:
socketToServer.connect(new InetSocketAddress(ServerIP,ServerPort),SOCKET_TIMEOUT);
3- ورودی و خروجی را مشخص کنید:
BufferedReader inputStream = new BufferedReader(new InputStreamReader(socketToServer.getInputStream(), "UTF-8"));
DataOutputStream outputStream = new DataOutputStream(socketToServer.getOutputStream());
4- اطلاعات مورد نظرتان را از ورودی دریافت و یا به خروجی ارسال کنید :
outputStream.write(outData.getBytes("UTF-8"));
inData = inputStream.readLine();
اصل دریافت و ارسال اطلاعات از سوکت همین ها است. ولی خب این وسط یک سری قابلیت هایی نیاز هست که شما مجبور می شوید چندین خط کد دیگر نیز به آنها اضافه کنید.
در زیر من یک کلاس به عنوان کلاینت تعریف کرده ام. این کلاس توسط یک Thread مدیریت می شود تا خللی در پردازش اصلی برنامه ایجاد نکند.
یک فایل جاوای جدید به نام ClientSocket.java ایجاد کنید و اطلاعات زیر را داخل آن بریزید
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.Socket;
public class ClientSocket {
private Socket socketToServer;
private DataOutputStream outputStream;
private BufferedReader inputStream;
private int connectionStatus; //0= Disconnected , 1= Connected , 2= Connecting
private boolean loop;
Thread t;
private OnServerConnected mOnServerConnected;
private OnServerDisconnected mOnServerDisconnected;
private OnServerDataRecieve mOnServerDataRecieve;
String sIP;
String sPort;
public interface OnServerConnected {
void onConnect();
}
public interface OnServerDisconnected {
void onDisconnect();
}
public interface OnServerDataRecieve {
void onDataRecived(String Data);
}
public void setOnServerConnected(OnServerConnected eventListener) {
mOnServerConnected = eventListener;
}
public void setOnServerDisconnected(OnServerDisconnected eventListener) {
mOnServerDisconnected = eventListener;
}
public void setOnServerDataRecieve(OnServerDataRecieve eventListener) {
mOnServerDataRecieve = eventListener;
}
public ClientSocket(String serverIP, int serverPort ) {
sIP=serverIP;
sPort=serverPort;
}
public int getConnectionStatus() {
return connectionStatus;
}
public boolean isConnected() {
if (connectionStatus == 1)
return true;
else
return false;
}
public void connectToServer() {
if (t == null) {
t = new Thread() { // Read Data
@Override
public void run() {
loop = true;
while (loop) {
try {
connectionStatus = 2;
socketToServer = new Socket();
socketToServer.connect(new InetSocketAddress(sIP, sPort), 6000);
outputStream = new DataOutputStream(socketToServer.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socketToServer.getInputStream(), "UTF-8"));
connectionStatus = 1;
if (mOnServerConnected != null)
mOnServerConnected.onConnect();
StartListen();
}
catch (Exception e) {
} finally {
if (connectionStatus == 1 && mOnServerDisconnected != null)
mOnServerDisconnected.onDisconnect();
connectionStatus = 0;
try {
if ( !socketToServer.isClosed())
socketToServer.close();
socketToServer = null;
outputStream = null;
inputStream = null;
}
catch (IOException e1) {
}
catch (Exception e1) {
}
try {
// waite for a few seccond to next try
Thread.sleep(10000);
}
catch (InterruptedException e) {
}
}
}
t = null;
}
};
t.start();
}
}
public void stop() {
loop = false;
try {
socketToServer.close();
}
catch (Exception e) {
}
}
public boolean sendMessage(String data) {
if (outputStream == null || connectionStatus != 1) {
return false;
}
try {
outputStream.write((data + "\n").getBytes("UTF-8"));
outputStream.flush();
}
catch (Exception e) {
try {
socketToServer.close();
}
catch (Exception e2) {
}
return false;
}
return true;
}
private void StartListen() {
while (true) {
if (socketToServer.isClosed()) {
connectionStatus = 0;
break;
}
String message = "";
try {
message = inputStream.readLine();
}
catch (IOException e1) {
}
if (message == null)
else if (message.length() == 0)
if ( !(message == null) && !message.equals("")) {
if (mOnServerDataRecieve != null)
mOnServerDataRecieve.onDataRecived(message);
}
else {
break;
}
}
}
}
socketToServer.connect(new InetSocketAddress(ServerIP,ServerPort),SOCKET_TIMEOUT);
اگر دامنه دارید ، به جای ServerIP
اسم دامنه یا همان DNS
تان را هم می توانید ذکر کنید. اصلا هم نیازی به پورت فورواردینگ نیست. ولی اگر سرور تان با دامنه قابل دسترس نیست مبحث کمی تخصصی می وشد و باید ساختار شبکه بیشتر بررسی شود.
(7 سال پیش)
سمت سرور:
در این سمت هم مثل سمت کلاینت کافی است چیزی شبیه به سمت کلاینت اجرا کنید:
1- یک گیرنده (Listener) برای دریافت سوکت های ورودی تعریف کنید :
ServerSocket server = new ServerSocket(PORT);
2- گیرنده را فعال کنید و پس از دریافت هر ارتباط ورودی آن را به یک سوکت باز تبدیل کنید:
Socket socket = server.accept();
3- از اینجا به بعد مثل کلاینت ورودی و خروجی را مشخص کنید:
BufferedReader inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream());
4- اطلاعات مورد نظرتان را از ورودی دریافت و یا به خروجی ارسال کنید :
outputStream.write((outData).getBytes("UTF-8"));
inData = inputStream.readLine();
حالا این وسط یک مشکلی وجود دارد که خیلی هم فنی است :
اگر لازم شد بیش از یک نفر به سرور وصل شود چه بکنیم ؟
این مشکلی است که کمی درک بیشتری از برنامه نویسی نیاز داریم تا بتوانیم آن را مدیریت کنیم.
من برای حل این موضوع از دو کلاس مجزا استفاده کرده ام. یک کلاس به عنوان مدیر جهت دریافت کانکشن ها و تبدیل آنها به سوکت و یک کلاس دیگر برای مدیریت اختصاصی هر کدام از سوکت های دریافتی !
کلاس زیر به عنوان یک مدیر کل کانکشن ها را دریافت می کند و به ازای هر دستگاه جدیدی که درخواست اتصال می کند یک شیء جدید از کلاس موبایل ( که قرار است هر کدام از سوکت های دریافتی را توسط آن مدیریت کنیم) ایجاد می کند.
من آن کلاس مربوط به کلاینت هایی که درخواست اتصال می دهند را Mobile و کلاس مدیر آنها را MobileManger نام گذاری کرده ام
import com.example.myserverapp.Mobile.OnMobileConnected;
import com.example.myserverapp.Mobile.OnMobileDataRecieve;
import com.example.myserverapp.Mobile.OnMobileDisconnected;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import android.app.IntentService;
import android.content.Intent;
import android.util.SparseArray;
public class MobileManager extends IntentService {
private SparseArray<Mobile> onlineMobiles = new SparseArray<Mobile>();
ServerSocket mobilesSocket = null;
public MobileManager(String name) {
super(name);
startServer();
}
public MobileManager() {
super("MobileManager");
startServer();
}
public void startServer(final int Port) {
new Thread(new Runnable() {
@Override
public void run() {
try {
mobilesSocket = new ServerSocket(Port);
while (true) {
Socket socket = mobilesSocket.accept();
Mobile mobile = new Mobile(socket);
mobile.setOnMobileConnected(new OnMobileConnected() {
@Override
public void onEvent(Mobile mobile) {
int mobileID = mobile.getMobileID();
if (onlineMobiles.indexOfKey(mobileID) > 0) {
onlineMobiles.remove(mobileID);
}
onlineMobiles.put(mobileID, mobile);
}
});
mobile.setOnMobileDisconnected(new OnMobileDisconnected() {
@Override
public void onEvent(Mobile mobile) {
int mobileID = mobile.getMobileID();
if (onlineMobiles.indexOfKey(mobileID) > 0) {
onlineMobiles.remove(mobileID);
}
}
});
mobile.setOnMobileDataRecieved(new OnMobileDataRecieve() {
@Override
public void onEvent(Mobile mobile, String data) {
// من در اینجا اطلاعات را برای پردازش به یک کلاس دیگر می فرستم
MessageParser.parseFromMobile(mobile.getMobileID(), data.trim());
}
});
mobile.startlisten();
}
}
catch (IOException e) {
} finally {
try {
mobilesSocket.close();
}
catch (Exception e) {
}
}
}
}).start();
}
public void disconnectAll() {
for (int i = 0; i < onlineMobiles.size(); i++)
onlineMobiles.get(onlineMobiles.keyAt(i)).disconnect();
}
public void disconnectMobile(int mobileID) {
if (onlineMobiles.indexOfKey(mobileID) >= 0)
onlineMobiles.get(mobileID).disconnect();
}
public void stopServer() {
disconnectAll();
try {
mobilesSocket.close();
}
catch (IOException e) {
}
}
public void sendData(String data, int MobileID) {
if (onlineMobiles.indexOfKey(MobileID) >= 0)
onlineMobiles.get(MobileID).sendToMobile(data);
}
@Override
protected void onHandleIntent(Intent workIntent) {
}
}
این کلاس به عنوان یک سرویس پیاده شده که بتوانید آن را در پس زمینه گوشی نیز اجرا کنید. در این حالت دیگر نیازی نیست برنامه باز باشد . (توضیحات مربوط به سرویس را در فیلم های استاد ببینید)
و هم اکنون نیز کلاس Mobile که وظیفه مدیریت هر کدام از سوکت های دریافتی را دارد.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class Mobile {
private int mobileID = 0;
private Socket socket;
private DataOutputStream outputStream;
private BufferedReader inputStream;
private int status = 0; // 0= disconnected 1= connected 2= Trying
private OnMobileConnected mOnMobileConnected;
private OnMobileDisconnected mOnMobileDisconnected;
private OnMobileDataRecieve mOnMobileDataRecieve;
public interface OnMobileConnected {
void onEvent(Mobile mobile);
}
public interface OnMobileDisconnected {
void onEvent(Mobile mobile);
}
public interface OnMobileDataRecieve {
void onEvent(Mobile mobile, String data);
}
public void setOnMobileConnected(OnMobileConnected eventListener) {
mOnMobileConnected = eventListener;
}
public void setOnMobileDisconnected(OnMobileDisconnected eventListener) {
mOnMobileDisconnected = eventListener;
}
public void setOnMobileDataRecieved(OnMobileDataRecieve eventListener) {
mOnMobileDataRecieve = eventListener;
}
public Mobile(Socket socket) {
this.socket = socket;
try {
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
status = 1;
}
catch (IOException e1) {
status = 2;
}
}
private void dataReciever(String data) {
if (mobileID == 0) {
try {
// من در اینجا فرض را بر این گذاشته ام که هر کلاینتی که متصل می شود
// اولین پیامی که ارسال می کند شماره آی دی اش باشد تا بتوانم بهتر آن
// دستگاه را مدیریت کنم.
JSONArray jArray = new JSONArray(data);
JSONObject jObject = jArray.getJSONObject(0);
int id = jObject.getInt("MobileID");
mobileID = id;
if (mOnMobileConnected != null)
mOnMobileConnected.onEvent(this);
}
catch (JSONException e) {
}
return;
}
//-----------------------------------------------------------------------------
if (mOnMobileDataRecieve != null)
mOnMobileDataRecieve.onEvent(this, data);
}
public boolean sendToMobile(String data) {
if (socket.isClosed()) {
return false;
}
try {
outputStream.write((data + "\n").getBytes("UTF-8"));
outputStream.flush();
}
catch (IOException e) {
return false;
}
return true;
}
public void startlisten() {
if (status == 1)
new Thread(new Runnable() {
@Override
public void run() {
listen();
onDisconnect();
}
}).start();
}
private void listen() {
while (true) {
try {
if (socket.isClosed()) {
status = 0;
return;
}
status = 1;
String data = "";
data = inputStream.readLine();
if ( !(data == null) && data.length() > 0) {
dataReciever(data);
}
else {
break;
}
}
catch (IOException e) {
return;
}
}
}
public void disconnect() {
try {
socket.close();
}
catch (IOException e) {
}
}
private void onDisconnect() {
try {
if (socket.isConnected())
socket.close();
}
catch (IOException e) {
}
if (mOnMobileDisconnected != null)
mOnMobileDisconnected.onEvent(this);
}
public int getStatus() {
return status;
}
public int getMobileID() {
return mobileID;
}
}
سلام . ویدئو آموزشی سوکت نویسی که توسط مهندس آقاجانی ارائه شد کجاست ؟ تو پکیج نبود /
کد های سمت سرور چطور میشه ؟ اصلا کد نیاز هست ؟ من VPS لینوکسی دارم ، اون رو چطور پیکربندی کنم ؟
پاسخگویی و مشاهده پاسخ های این سوال تنها برای اعضای ویژه سایت امکان پذیر است .
چنانچه تمایل دارید به همه بخش ها دسترسی داشته باشید میتوانید از این بخش لایسنس این آموزش را خریداری نمایید .