いくつかの提案があります。私の例は実用的なコードではなく、あなたのための作業ベースにすぎません。
まず、サーバーでスレッドを使用することを強くお勧めします。クライアントがサーバーに接続するたびに、スレッドを開始するために必要なすべてのデータを含むパラメーターを使用して、新しいスレッドを作成します。
boolean running = true; //this class variable will allow you to shut down the server correctly
public void stopServer(){ //this method will shut down the server
this.running = false;
}
public void run() {
...
while(running) {
// if(this.connection == null) { // I removed this line since it's unnecessary, or even harmful!
StreamConnection connection = this.server.acceptAndOpen(); //This line will block until a connection is made...
System.out.println("INFO: Bluetooth client connected");
Thread thread = new ServerThread(connection);
thread.start() //don't forget exception handling...
}
}
クラス ServerThread では、クライアントを処理する次の行を実装します (コンパイルされていないコード、例外処理なし!):
Class ServerThread extends Thread {
StreamConnection connection;
public ServerThread(StreamConnection connection){
this.connection = connection;
}
public void run() {
...
connection.close(); //closing the connection...don't forget exception handling!
System.out.println("INFO: Client disconnected");
}
}
このコードの利点は何ですか? サーバーは、同時に 1000 のクライアントを処理できるようになりました。並列化ができました。これがサーバーの通常の動作です。スレッドのないサーバーは、靴のない靴下のようなものです...
次に、Java クライアントと Java サーバーを使用している場合は、ObjectOutputStream/ObjectInputStream という、より簡単にオブジェクトをサーバーに送信する方法を使用できます。連絡先を含む配列 (通常は ArraList を使用します) をサーバーに送信するだけで、その配列を読み取ることができます。サーバーのコードは次のとおりです (ここでもコンパイルされておらず、例外処理はありません)。
Class ServerThread extends Thread {
StreamConnection connection;
public ServerThread(StreamConnection connection){
this.connection = connection;
}
public void run() {
BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
ObjectInputStream ois = new ObjectInputStream(bis);
ArrayList contacts = (ArrayList) ois.readObject(); //this is a cast: don't forget exception handling!
//You could also try the method ois.readUTF(); especially if you wanna use other non-Java clients
System.out.println("INFO: Received from Bluetooth: " + contacts);
this.connection.close(); //closing the connection...don't forget exception handling!
//ois.close(); //do this instead of "this.connection.close()" if you want the connection to be open...i.e. to receive more data
System.out.println("INFO: Client disconnected");
//here you do whatever you wanna do with the contacts array, maybe add to your other contacts?
}
}
Java では、ArrayList を含むすべてのクラスがオブジェクトです。また、オブジェクトの終了は切断と見なされるため、他に何もする必要はありません。
3 番目: 上記のサーバーは、Bluetooth 接続だけでなく、WLAN 接続にも使用します。次に、疑似コードのように、さまざまなスレッドを簡単に開始できますif(connection.isBluetooth()){//create a thread from BluetoothThread} else if(connection.isWLAN()){//create a thread from WLANsThread}
。アプリの内容はわかりませんが、いつかデスクトップ PC に拡張したいので、WLAN を使用するのが適切です。また、とにかくクライアントに検証を組み込む必要があるため (「どの連絡先がどのサーバーに送信されるのか?」)、それが Bluetooth であろうと WLAN であろうと、buetooth の範囲が狭いとセキュリティを確保できないためです。 . ;)
最後に、質問について: 何かを取得するには、データ ソースおよび/またはクラス変数が必要です。連絡先を保存するファイルの短い例を次に示します (ただし、ローカルまたは別の場所のデータベースにすることもできます!):
public class MyApp(){
ArrayList contacts;
...
public void run(){ //this happens when we start our app
this.contacts = new ArrayList();
FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
BufferedReader br = new BufferedReader(fr);
//here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
}
//now we want to send our contacts array to the already connected server:
public sendArrayToServer() {
BufferedOutputStream bos = new BufferedOutputStream (this.connection.openOutputStream());
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this.contacts);
//If you use readUTF() in the server, you need to call here something like oos.writeUTF(this.contacts.toString()); or even need to use another parser method which exactly creates the string you want.
this.connection.close(); //closing the connection...don't forget exception handling!
//oos.close(); //do this instead of "this.connection.close()" if you want the connection to stay open...
}
}
サーバーでは、既に説明したように連絡先配列を読み取るだけです。それらの連絡先をどうするかは、あなた次第です。
これが問題を理解し、解決策を見つけるのに役立つことを願っています。プログラミングは、試行錯誤とコードの改善がすべてです。
編集:
議論の後、私は最終的に必要なものを見つけました: GUIController と呼ばれる別のスレッドと対話する BluetoothManager と呼ばれる 1 スレッド サーバーが必要です。とにかく頭の中で実装を行ったので、いくつかの説明とともに投稿できます。この場合、サーバーで別のスレッドを初期化する必要はないことに注意してください。これは、BluetoothManager が既にスレッドであり、同時に必要な接続は 1 つだけであるためです (それが「サーバー」であるかどうかという問題が残ります)。 、むしろ「レシーバー」と呼びます):
Public class BluetoothManager extends Thread{
boolean running = true; //this class variable will allow you to shut down the server correctly
GUIController controller;
public BluetoothManager(GUIController controller){
this.controller = controller; //this registers the GUIController in the BluetoothManager
}
public void stop(){ //this method will shut down the "server"
this.running = false;
}
public void run() {
this.localDevice = LocalDevice.getLocalDevice();
this.localDevice.setDiscoverable(DiscoveryAgent.GIAC);
this.server = (StreamConnectionNotifier) Connector.open(URL);
while(running){
StreamConnection connection = this.server.acceptAndOpen(); //This line will block until a connection is made...or running==false!
System.out.println("INFO: Bluetooth client connected");
BufferedInputStream bis = new BufferedInputStream(this.connection.openInputStream());
ObjectInputStream ois = new ObjectInputStream(bis);
ArrayList contacts = (ArrayList) ois.readObject(); //this is a cast: don't forget exception handling!
System.out.println("INFO: Received from Bluetooth: " + contacts);
this.connection.close(); //closing the connection...don't forget exception handling!
System.out.println("INFO: Client disconnected");
this.controller.refreshContacts(contacts);
}
}
}
public class GUIController extends Thread implements Runnable {
ArrayList contacts; //also a HashMap may be appropriate
BluetoothManager manager;
public void run(){
this.contacts = new ArrayList();
FileReader fr = new FileReader ("C:\WhereverYourFileIs\Contacts.file");
BufferedReader br = new BufferedReader(fr);
//here you use a loop to read the contacts via "br" from the file and fill them into your array...I can't provide you more code, since the exact data structure is up to you.
}
public void startBluetoothManager(){ //starting the BluetoothManager
this.manager = new BluetoothManager(this);
this.manager.start();
}
public void abortBluetoothManager(){ //call this when clicking on the "Abort" button
this.manager.stop();
//now the next 2 lines you normally don't need...still may use it if you've problems shutting down the thread:
// try{ this.manager.interrupt(); } //we want to be 100% sure to shut down our thread!
// catch(Exception e){}
this.manager = null; //now the garbage collector can clean everything...byebye
}
public void refreshContacts(ArrayList contacts) {
// synchronize(this.contactArray){ //no synchronisation needed if you have a GUI pop-up with an "Abort"-button!
Iterator i = this.contacts.iterator();
while(i.hasNext()){
this.contacts.add(i.next());
}
//At the end you need remove the "Receiving message" pop-up together with the "Abort Receiving"-button, these are all class variables!
// important note: If you have unique entries, you may need to replace them! In this case I suggest storing all contact objects better in a HashMap contacts, and use the unique ID as a key to find the element. And then you may prompt the user, if there are identical entries, to overwrite each entry or not. These things remain all up to you.
}
}
//As always: This is no compiled code!!
GUIController は最初に BluetoothManager を実行し、startBluetoothManager()
「連絡先を受信中」という通知と「受信を中止」ボタンを表示する以外は何もしません。BluetoothManager が終了したら、新しい連絡先を GUIController 内の既存の連絡先配列に追加するだけrefreshContacts(...)
です。「Abort Reveiving」ボタンを押すと、すぐにabortBluetoothManager()
メソッドが呼び出され、BluetoothManager に設定running=false
されてサーバーが終了し、スレッドが終了します。
このソリューションが解決する主な問題は、2 つのスレッドが互いに直接通信できないことです。を呼び出すとthread.start()
、すべてのスレッドが独立します。そのため、BluetoothManager スレッドが GUIController スレッドに「終了しました!」と伝える可能性はありません。これらのスレッドができる唯一のことは、同じリソースを共有し、このリソースを介して通信することです。私たちの場合、それはcontacts
GUIController の -ArrayList であり、最初に同期する必要があり、両方のスレッドで更新できると考えました (ただし、同時にではありません)。そして-ちょっと面白い-2番目の共有リソースがあります。実際には、running
それをシャットダウンできるBluetoothManagerクラスのフラグです(ただし、running
必要な同期は決してありません。
同期について:私はこの問題についてもっと考え、「同期(...)」呼び出しなしでも問題を解決できることを理解しました。したがって、ArrayList を同期させたくない場合は、次のようにする必要があります。サーバーの実行中は、[連絡先の受信] ポップアップと [受信の中止] ボタンのみを表示します。これが発生している間は、GUIController 内の contact-ArrayList にアクセスすることはありません。これはどういうわけか、実際の Java 同期を必要としない「本質的な同期」です。それでも、将来アプリを拡張しても何も起こらないことを 100% 確実にするために、同期を実装することはできます。