* Пример за приложения използващи сокети
Публикувано на 31 октомври 2011 в раздел ПИК3 Java.
В представеното приложение е реализирана възможно най-базовата комуникация между клиент и сървър, при която се разменят поредица от текстови съобщения. Обърнете внимание, че не се използват нишки, т.е. за една "сесия" сървърът може да комуникира само с един клиент.
Server.java:
import java.io.PrintWriter; import java.net.Socket; import java.net.ServerSocket; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.Scanner; public class Server{ public static void main(String args[]) { ServerSocket serverSocket = null; Socket connection = null; Scanner socketIn = null; PrintWriter socketOut = null; int port = 1234; // Безкраен цикъл - сървъра ще приема връзки // докато програмата не бъде спряна принудително while(true){ try { serverSocket = new ServerSocket(port); System.out.println("Сървъра очаква свързване..."); connection = serverSocket.accept(); System.out.println("Свърза се клиент:" +connection.getInetAddress().getHostName() ); System.out.println("Подавам съобщение на клиента..."); socketOut = new PrintWriter(connection.getOutputStream(), true); socketOut.println("Възможните команди са \"hi\" и \"exit\""); System.out.println("Сървъра очаква подаване на команда..."); socketIn = new Scanner(new BufferedReader( new InputStreamReader(connection.getInputStream())) ); String command = null; loop: do{ socketOut.flush(); command = socketIn.nextLine(); switch(command){ case "hi": System.out.println("Клиентът подаде команда \"hi\""); socketOut.println("OK"); break; case "exit": System.out.println("Клиентът подаде команда \"exit\""); socketOut.println("OK"); break loop; default: System.out.println("Получих непозната команда"); socketOut.println("Не познавам тази команда"); break; } } while(!command.equalsIgnoreCase("exit")); System.out.println("Затварям връзката с " +connection.getInetAddress().getHostName() ); } catch(IOException e) { e.printStackTrace(); } finally{ try{ if (socketIn!=null) socketIn.close(); if (socketOut!=null) socketOut.close(); if (connection!=null) connection.close(); if (serverSocket!=null) serverSocket.close(); } catch(IOException e){ System.err.println("Не може да бъде затворен сокет"); } } } } }
Client.java:
import java.io.PrintWriter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.Scanner; import java.net.Socket; import java.io.IOException; import java.net.ConnectException; public class Client{ public static void main(String args[]){ Socket connection = null; Scanner socketIn = null; PrintWriter socketOut = null; Scanner keyboardIn = new Scanner(System.in); int port = 1234; String host = "localhost"; try{ System.out.println("Очаквам свързване със сървъра"); try{ connection = new Socket(host, port); } catch(ConnectException e){ System.err.println("Не мога да осъществя връзка със сървъра"); return; } System.out.println("Свързването със сървъра беше успешно!"); System.out.println("Приемам първоначално съобщение от сървъра..."); socketIn = new Scanner(new BufferedReader( new InputStreamReader(connection.getInputStream())) ); System.out.println("Съобщението е: "+socketIn.nextLine()); String command = null; socketOut = new PrintWriter(connection.getOutputStream(), true); do{ socketOut.flush(); System.out.print("Въведете команда: "); command = keyboardIn.nextLine(); socketOut.println(command.toLowerCase()); System.out.println("Изчакване на отговор от сървъра..."); String answer = socketIn.nextLine(); System.out.println("Сървърът отговори: "+answer); } while (!command.equalsIgnoreCase("exit")); System.out.println("Затваряне на връзката..."); } catch(IOException e) { e.printStackTrace(); } finally{ try{ if(socketIn!=null) socketIn.close(); if(socketOut!=null) socketOut.close(); if(connection!=null) connection.close(); } catch(IOException e){ System.err.println("Не може да се затвори сокета"); } } } }
В Java SE 7 работи. Свалете си и използвайте JDK7. Не би трябвало да има значение дали средата е Eclipse или друга.
За повече информация: http://download.oracle.com/javase/7/docs/technotes/guides/language/strings-switch.html
Само че не знам дали switch ще работи, тъй като Еклипс ми иска int или enum, a ние подаваме String
socketIn = new Scanner(new BufferedReader(
new InputStreamReader(connection.getInputStream()))
);
на този ред(от Сървър класа) ми дава грешка
А на мен не.
П.П. Ако има нужда от помощ, то е редно да се напишат поне малко подробности:
1. Коя версия на JDK се използва?
2. Каква е грешката?
Определено има нещо нередно в твоята инсталация. Тази конструкция е съвсем валидна. Scanner си има конструктор приемащ "InputStream" като вход. Виж дали ще можеш да компилираш това:
http://www.java2s.com/Code/Java/File-Input-Output/newScannernewBufferedReadernewFileReaderxanadutxt.htm
JDK 1.7, а среедата е Eclipse.
Грешката която дава е:
- The constructor Scanner(BufferedReader) is undefined
- BufferedReader cannot be resolved to a type
- InputStreamReader cannot be resolved to a type
Сега се оправи като го написах наново. Явно средата не и е било до тези обекти одеве.
При мен, обаче, не се получава. Макар и да казвам на Еклипс, че трябва да рънва двата файла с 1.7 (дори и на програмата с комплексните числа), не се получава. Не приема в switch да имам String
Проблемът е очевидно в Eclipse. Ето тук са писали за проблема:
http://stackoverflow.com/questions/6231907/java-7-switch-statement-with-strings-not-working
Винаги е така с новостите - трудно се внедряват.
Благодаря, ще коригирам.
Не се казва "насилствено", "принудително". Всяко нещо трябва да се назовава точно.
Защо при скенера използваме InputStreamReader, за да превърнем байтовия поток в символен, а при PrintWriter не използваме OutputStreamWriter т.е. socketOut=new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), true); ?
Просто ще работи директно с потока - без буфер.
Това "превръщане" в примера от статията всъщност е паразитно и ненужно, но да - забележката е уместна, би трябвало и двете да са в един стил.
Предимството да "минава през" InputStreamWriter/OutputStreamWriter е, че по този начин може да се дефинира какъв character set се използва. В примера не е дефинирано тъй или иначе, т.е. ще се използва стандартния.
Пример как трябва да бъдат:
socketOut = new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"), true);
socketIn = new Scanner(new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")));
П.П. PrintWriter даже май си има собствен буфер, вероятно затова не е слаган BufferedWriter... В документацията на сайта на Oracle ще пише дали е така.
Не ставаше въпрос за BufferedReader, а защо при скенера използваме InputStreamReader за превръщане на байтовия поток в символен, а при PrintWriter НЕ използваме OutputStreamWriter за превръщане на байтовия поток в символен?