* Пример за приложения използващи сокети
Публикувано на 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 за превръщане на байтовия поток в символен?