我正在實現一個使用Java的服務器/客戶機系統。服務器監聽來自客戶端的連接和客戶端連接後,服務器創建一個新的套接字並將其傳遞給一個新的線程,這將只用於接收數據:爲什麼Java發送需要Thread.Sleep
while (true){
clientSocket=serverSocket.accept();
new ClientReceiver(clientSocket,this.clientsManager).start();
}
的clientReceiver類如下:
public class ClientReceiver extends Thread {
private Socket clientSocket=null;
private Client client=null;
private ClientsManager clientsManager;
private ClientActionParser clientActionParser=new ClientActionParser();
ClientHandlerState clientHandlerState;
PrintWriter outputStream=null;
BufferedReader inputStream=null;
public ClientReceiver(Socket clientSocket, ClientsManager clientsManager){
this.clientSocket=clientSocket;
this.clientsManager=clientsManager;
this.setClientHandlerState(ClientHandlerState.Connected);
}
public void run(){
String actionString;
try{
//define output and input stream to client
outputStream =new PrintWriter(clientSocket.getOutputStream(),true);
inputStream = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
//while client is connected read input to actionString
while((actionString=inputStream.readLine()) != null){
AbstractClientAction clientAction= this.clientActionParser.parse(actionString);
if(this.getClientHandlerState()==ClientHandlerState.Connected){
if(clientAction instanceof ClientLoginAction){
ClientLoginAction clientLoginAction=(ClientLoginAction) clientAction;
if(this.authenticate(clientLoginAction)){
}
else{
throw new AuthenticationException();
}
}
else{
throw new AuthenticationException();
}
}
}
if(this.getClientHandlerState()==ClientHandlerState.Authorized){
//receive other client actions: transfer barge ....
}
try {
Thread.sleep(400);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch(IOException e){
}
catch (AuthenticationException e) {
// TODO: handle exception
}
//clean up the resources
try{
outputStream.close();
inputStream.close();
clientSocket.close();
}
catch(Exception e){
}
}
private boolean authenticate(ClientLoginAction clientLoginAction){
//perform authentication. If authentication successful:
this.client=this.clientsManager.authenticateClient(clientLoginAction.getUsername(), clientLoginAction.getPassword());
if(this.client==null){
return false;
}
else{
ClientSender clientSender=new ClientSender(this.outputStream, this.client);
this.clientsManager.addClientSender(clientSender);
this.setClientHandlerState(ClientHandlerState.Authorized);
clientSender.start();
return true;
}
}
public ClientHandlerState getClientHandlerState(){
return this.clientHandlerState;
}
public void setClientHandlerState(ClientHandlerState clientHandlerState){
this.clientHandlerState=clientHandlerState;
}
在接收線程成功驗證之後,將數據發送到客戶端和套接字的OutputStream傳遞給新的線程創建一個新的線程。 clientSender類包含一個隊列作爲包含應發送給客戶端的數據的緩衝區。這裏是類clientSender:
public class ClientSender extends Thread {
private Client client=null;
private final Log logger = LogFactory.getLog(getClass());
PrintWriter outputStream=null;
private Queue<String> clientEventsQueue= new LinkedList<String>();
public ClientSender(PrintWriter outputStream, Client client){
this.outputStream=outputStream;
this.client=client;
}
public void run(){
//System.out.println("ClientSender run method called.");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(!this.clientEventsQueue.isEmpty()){
this.outputStream.println(this.clientEventsQueue.remove());
}
}
}
public Client getClient(){
return this.client;
}
public void insertClientEvent(String clientEvent){
this.clientEventsQueue.add(clientEvent);
}
每當我想送點東西給客戶使用:
clientSender.insertClientEvent("some text");
的問題是,如果我刪除了Thread.sleep(10)我將不會收到任何東西客戶端。由於TCP套接字阻塞,我認爲這不應該發生。這是正常的還是我做錯了什麼?
編輯: 發件人線程沒有「終止」。無論何時從其他系統收到事件,服務器都應向所有客戶端發送適當的信息。所以我認爲最好的方案是在沒有數據要發送時停止線程,並在任何時候啓動它。所以我在clientSender類中試過這個:
public void run(){
while(true){
if(this.clientEventsQueue.isEmpty()){
break;
}
else{
try {
this.outputStream.println(this.clientEventsQueue.take());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
但現在問題是何時啓動線程?我試圖啓動它,每當我想發送數據,但如我所料不正常工作,並且只發送拳包:
clientSender.insertClientEvent(clientEvent.getEventString());
clientSender.start();
EDIT2 我想出了這個主意。這非常簡單,我認爲它消耗的CPU時間少得多。
while(true){
while(this.clientEventsQueue.isEmpty()){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
this.outputStream.println(this.clientEventsQueue.take());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
就像我測試它,它工作得很好。你怎麼看待這件事?
您正在使用哪種操作系統? –
可能是種族危害。不安全的發佈到線程? – Raedwald
用你發佈的內容很難猜測你的代碼有什麼問題。你顯然有某種地方的競爭條件。或者其他的東西。 –