2016-09-06 7 views
0

私はScalaでチャットアプリケーションを作成していますが、問題はクライアントであり、クライアントはStdIn(ブロック)から読み込み、エコーサーバーにデータを送信します。 StdInからの読み取りが完了するまでサーバーからデータを受信しません。私は地元のIO、つまりはSTDINからの読み込みおよびソケットへの書き込み/読み込みが別のスレッドでなければならないことを考えているが、私は以下、これを行うための方法を考えることはできませんクライアントシングルトンコードです:スカラチャットアプリケーション、ブロック問題

import java.net._ 
import scala.io._ 
import java.io._ 
import java.security._ 

object Client { 

    var msgAcc = "" 

    def main(args: Array[String]): Unit = { 
    val conn = new ClientConnection(InetAddress.getByName(args(0)), args(1).toInt) 
    val server = conn.connect() 
    println("Enter a username") 
    val user = new User(StdIn.readLine()) 
    println("Welcome to the chat " + user.username) 
    sys.addShutdownHook(this.shutdown(conn, server)) 
    while (true) { 
    val txMsg = StdIn.readLine()//should handle with another thread? 
    if (txMsg != null) { 
     conn.sendMsg(server, user, txMsg) 
     val rxMsg = conn.getMsg(server) 
     val parser = new JsonParser(rxMsg) 
     val formattedMsg = parser.formatMsg(parser.toJson()) 
     println(formattedMsg) 
     msgAcc = msgAcc + formattedMsg + "\n" 
     } 
    } 
    } 

    def shutdown(conn: ClientConnection, server: Socket): Unit = { 
    conn.close(server) 
    val fileWriter = new BufferedWriter(new FileWriter(new File("history.txt"), true)) 
    fileWriter.write(msgAcc) 
    fileWriter.close() 
    println("Leaving chat, thanks for using") 
    } 

} 

import java.net._ 
import scala.io._ 
import java.io._ 
import java.security._ 
import java.util.NoSuchElementException 

object Client { 

    var msgAcc = "" 

    def main(args: Array[String]): Unit = { 
    val conn = new ClientConnection(InetAddress.getByName(args(0)), args(1).toInt) 
    val server = conn.connect() 
    println("Enter a username") 
    val user = new User(StdIn.readLine()) 
    println("Welcome to the chat " + user.username) 
    sys.addShutdownHook(this.shutdown(conn, server)) 
    new Thread(conn).start() 
    while (true) { 
    val tx = conn.tx 
    if (tx != null) { 
     conn.sendMsg(server, user, tx) 
     val rxMsg = conn.getMsg(server) 
     val parser = new JsonParser(rxMsg) 
     val formattedMsg = parser.formatMsg(parser.toJson()) 
     println(formattedMsg) 
     msgAcc = msgAcc + formattedMsg + "\n" 
     } 
    } 
    } 

    def shutdown(conn: ClientConnection, server: Socket): Unit = { 
    conn.close(server) 
    val fileWriter = new BufferedWriter(new FileWriter(new File("history.txt"), true)) 
    fileWriter.write(msgAcc) 
    fileWriter.close() 

これが実行可能に延びるClientConnectionクラスがある:

import javax.net.ssl.SSLSocket 
import javax.net.ssl.SSLSocketFactory 
import javax.net.SocketFactory 
import java.net.Socket 
import java.net.InetAddress 
import java.net.InetSocketAddress 
import java.security._ 
import java.io._ 
import scala.io._ 
import java.util.GregorianCalendar 
import java.util.Calendar 
import java.util.Date 
import com.sun.net.ssl.internal.ssl.Provider 
import scala.util.parsing.json._ 

class ClientConnection(host: InetAddress, port: Int) { 

    def connect(): Socket = { 
    Security.addProvider(new Provider()) 
    val sslFactory = SSLSocketFactory.getDefault() 
    val sslSocket = sslFactory.createSocket(host, port).asInstanceOf[SSLSocket] 
    sslSocket 
    } 

    def getMsg(server: Socket): String = new BufferedSource(server.getInputStream()).getLines().next() 

    def sendMsg(server: Socket, user: User, msg: String): Unit = { 
    val out = new PrintStream(server.getOutputStream()) 
    out.println(this.toMinifiedJson(user.username, msg)) 
    out.flush() 
    } 

    private def toMinifiedJson(user: String, msg: String): String = { 
    s"""{"time":"${this.getTime()}","username":"$user","msg":"$msg"}""" 
    } 

    private def getTime(): String = { 
    val cal = Calendar.getInstance() 
    cal.setTime(new Date()) 
    "(" + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND) + ")" 
    } 

    def close(server: Socket): Unit = server.close() 
} 

これは標準入力から読み取るようにスレッドを使用してクライアントシングルトンである:

は以下ClientConnectionクラスであり、

import javax.net.ssl.SSLSocket 
import javax.net.ssl.SSLSocketFactory 
import javax.net.SocketFactory 
import java.net.Socket 
import java.net.InetAddress 
import java.net.InetSocketAddress 
import java.security._ 
import java.io._ 
import scala.io._ 
import java.util.GregorianCalendar 
import java.util.Calendar 
import java.util.Date 
import com.sun.net.ssl.internal.ssl.Provider 
import scala.util.parsing.json._ 

class ClientConnection(host: InetAddress, port: Int) extends Runnable { 

    var tx: String = null 

    override def run(): Unit = { 
    tx = StdIn.readLine() 
    } 

    def connect(): Socket = { 
    Security.addProvider(new Provider()) 
    val sslFactory = SSLSocketFactory.getDefault() 
    val sslSocket = sslFactory.createSocket(host, port).asInstanceOf[SSLSocket] 
    sslSocket 
    } 

    def getMsg(server: Socket): String = new BufferedSource(server.getInputStream()).getLines().next() 

    def sendMsg(server: Socket, user: User, msg: String): Unit = { 
    val out = new PrintStream(server.getOutputStream()) 
    out.println(this.toMinifiedJson(user.username, msg)) 
    out.flush() 
    } 

    private def toMinifiedJson(user: String, msg: String): String = { 
    s"""{"time":"${this.getTime()}","username":"$user","msg":"$msg"}""" 
    } 

    private def getTime(): String = { 
    val cal = Calendar.getInstance() 
    cal.setTime(new Date()) 
    "(" + cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE) + ":" + cal.get(Calendar.SECOND) + ")" 
    } 

    def close(server: Socket): Unit = server.close() 
} 
+0

これまでに何を試しましたか? Javaのマルチスレッドについて何か知っていますか? – childofsoong

+0

ClientConnectionクラスをRunnableに拡張しようとしましたが(実装の代わりに拡張するscalaで)、run()メソッドがStdIn.readLine()を実行し、クライアントからアクセスしたクラス変数に結果を格納させますしかし、これは、クライアントからサーバーに送信されて戻ってくるデータのループを引き起こしました。私はマルチスレッドについて少し知っています。新しいクライアントが接続するたびに新しいスレッドを使用して、新しい接続(ブロック)とソケットIOをリスンするソケットが別々になるようにします。 – user2069328

+0

私は、あなたが間違ったことをその記述から知っていると思いますが、私が確信できるようにコードを投稿してください。私があなたを理解していれば、基本的にはStdIn入力が他のスレッドで更新されましたが、変更されたかどうかに関わらずメインスレッドで繰り返し送信されましたか? – childofsoong

答えて

0

だから、あなたは成功しRunnableへの入力の読み取りを移動したので、それは別のThread上で実行されますが、私たちはあなたのメインスレッド上のロジックを見たときに、今、我々はそれが常に送信されますことを確認しますそれがメッセージでない場合null。これにはいくつかの問題があります。

  • はあなたが単一のメッセージのみを受信しようとしているので、あなたは、run方法でループしていないし、お使いrun方法は終了 - あなたはAでこれをラップしたいですwhile(true)または​​ですので、更新してください。
  • サーバーにメッセージを送信しても、サーバーからのメッセージはまだ印刷されています。これを切り離して、サーバーへのメッセージの送信が他のスレッドで完全に行われるようにする必要があります。この線に沿って

何かがそれを解決する可能性があります:

//This is your new run method in your Runnable 
override def run(): Unit = { 
    while(true) { 
     tx = StdIn.readLine() 
     conn.sendMsg(server, user, tx) //Note you'll need to pass those references in somehow 
    } 
}` 

次に、あなたのメインスレッドでは、単にメッセージを取得し、それらをプリントアウトに対処:

new Thread(conn).start() 
while (true) { 
    //note the lack of sending messages in here 
    val rxMsg = conn.getMsg(server) 
    val parser = new JsonParser(rxMsg) 
    val formattedMsg = parser.formatMsg(parser.toJson()) 
    println(formattedMsg) 
    msgAcc = msgAcc + formattedMsg + "\n" 
} 

この方法では、 2つの動作は異なるスレッド上にあります。

+0

この問題を解決してくれてありがとうございました。今はサーバーのエコーデータのやり方に問題があります。それは接続されたクライアントのリストを保持し、そのリストで新しいスレッドをインスタンス化します。したがって、最初の接続では、その1つのクライアントへの参照のみがあり、2番目の接続では、新しいスレッドは両方のクライアントへの参照を持ち、2番目のクライアントはそれ自身と他のクライアントにデータを送信できます。最初のスレッドがその中の最初のクライアントのみを持つリストへの参照を持っているため、データ自体にデータがあります。 – user2069328

+0

@ user2069328それは別の質問にあるように聞こえます – childofsoong

+0

申し訳ありませんが、あなたの助けを歓迎します – user2069328

関連する問題