Java Language
Sieć
Szukaj…
Składnia
- nowe gniazdo („localhost”, 1234); // Łączy się z serwerem pod adresem „localhost” i portem 1234
- nowy SocketServer („localhost”, 1234); // Tworzy serwer gniazd, który może nasłuchiwać nowych gniazd pod adresem localhost i port 1234
- socketServer.accept (); // Akceptuje nowy obiekt Socket, którego można użyć do komunikacji z klientem
Podstawowa komunikacja klienta i serwera za pomocą gniazda
Serwer: Uruchom i poczekaj na połączenia przychodzące
//Open a listening "ServerSocket" on port 1234.
ServerSocket serverSocket = new ServerSocket(1234);
while (true) {
// Wait for a client connection.
// Once a client connected, we get a "Socket" object
// that can be used to send and receive messages to/from the newly
// connected client
Socket clientSocket = serverSocket.accept();
// Here we'll add the code to handle one specific client.
}
Serwer: obsługa klientów
Będziemy obsługiwać każdego klienta w osobnym wątku, aby wielu klientów mogło wchodzić w interakcje z serwerem w tym samym czasie. Ta technika działa dobrze, dopóki liczba klientów jest niska (<< 1000 klientów, w zależności od architektury systemu operacyjnego i oczekiwanego obciążenia każdego wątku).
new Thread(() -> {
// Get the socket's InputStream, to read bytes from the socket
InputStream in = clientSocket.getInputStream();
// wrap the InputStream in a reader so you can read a String instead of bytes
BufferedReader reader = new BufferedReader(
new InputStreamReader(in, StandardCharsets.UTF_8));
// Read text from the socket and print line by line
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}).start();
Klient: Połącz się z serwerem i wyślij wiadomość
// 127.0.0.1 is the address of the server (this is the localhost address; i.e.
// the address of our own machine)
// 1234 is the port that the server will be listening on
Socket socket = new Socket("127.0.0.1", 1234);
// Write a string into the socket, and flush the buffer
OutputStream outStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(outStream, StandardCharsets.UTF_8));
writer.println("Hello world!");
writer.flush();
Zamykanie gniazd i wyjątki dotyczące obsługi
Powyższe przykłady pominęły niektóre rzeczy, aby ułatwić ich czytanie.
Podobnie jak pliki i inne zasoby zewnętrzne, ważne jest, aby informować system operacyjny o ich zakończeniu. Kiedy skończymy z gniazdem, wywołaj
socket.close()
aby poprawnie je zamknąć.Gniazda obsługują operacje wejścia / wyjścia (wejścia / wyjścia), które zależą od różnych czynników zewnętrznych. Na przykład co, jeśli druga strona nagle się rozłączy? Co się stanie, jeśli wystąpi błąd sieci? Te rzeczy są poza naszą kontrolą. Dlatego wiele operacji na gniazdach może zgłaszać wyjątki, szczególnie
IOException
.
Bardziej kompletny kod dla klienta byłby więc mniej więcej taki:
// "try-with-resources" will close the socket once we leave its scope
try (Socket socket = new Socket("127.0.0.1", 1234)) {
OutputStream outStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(outStream, StandardCharsets.UTF_8));
writer.println("Hello world!");
writer.flush();
} catch (IOException e) {
//Handle the error
}
Serwer podstawowy i klient - pełne przykłady
Serwer:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Server {
public static void main(String args[]) {
try (ServerSocket serverSocket = new ServerSocket(1234)) {
while (true) {
// Wait for a client connection.
Socket clientSocket = serverSocket.accept();
// Create and start a thread to handle the new client
new Thread(() -> {
try {
// Get the socket's InputStream, to read bytes
// from the socket
InputStream in = clientSocket.getInputStream();
// wrap the InputStream in a reader so you can
// read a String instead of bytes
BufferedReader reader = new BufferedReader(
new InputStreamReader(in, StandardCharsets.UTF_8));
// Read from the socket and print line by line
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
catch (IOException e) {
e.printStackTrace();
} finally {
// This finally block ensures the socket is closed.
// A try-with-resources block cannot be used because
// the socket is passed into a thread, so it isn't
// created and closed in the same block
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Klient:
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Client {
public static void main(String args[]) {
try (Socket socket = new Socket("127.0.0.1", 1234)) {
// We'll reach this code once we've connected to the server
// Write a string into the socket, and flush the buffer
OutputStream outStream = socket.getOutputStream();
PrintWriter writer = new PrintWriter(
new OutputStreamWriter(outStream, StandardCharsets.UTF_8));
writer.println("Hello world!");
writer.flush();
} catch (IOException e) {
// Exception should be handled.
e.printStackTrace();
}
}
}
Ładowanie TrustStore i KeyStore z InputStream
public class TrustLoader {
public static void main(String args[]) {
try {
//Gets the inputstream of a a trust store file under ssl/rpgrenadesClient.jks
//This path refers to the ssl folder in the jar file, in a jar file in the same directory
//as this jar file, or a different directory in the same directory as the jar file
InputStream stream = TrustLoader.class.getResourceAsStream("/ssl/rpgrenadesClient.jks");
//Both trustStores and keyStores are represented by the KeyStore object
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
//The password for the trustStore
char[] trustStorePassword = "password".toCharArray();
//This loads the trust store into the object
trustStore.load(stream, trustStorePassword);
//This is defining the SSLContext so the trust store will be used
//Getting default SSLContext to edit.
SSLContext context = SSLContext.getInstance("SSL");
//TrustMangers hold trust stores, more than one can be added
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//Adds the truststore to the factory
factory.init(trustStore);
//This is passed to the SSLContext init method
TrustManager[] managers = factory.getTrustManagers();
context.init(null, managers, null);
//Sets our new SSLContext to be used.
SSLContext.setDefault(context);
} catch (KeyStoreException | IOException | NoSuchAlgorithmException
| CertificateException | KeyManagementException ex) {
//Handle error
ex.printStackTrace();
}
}
}
Zainicjowanie magazynu kluczy działa tak samo, z wyjątkiem zamiany dowolnego słowa Trust
w nazwie obiektu na Key
. Ponadto tablica KeyManager[]
musi zostać przekazana do pierwszego argumentu SSLContext.init
. To jest SSLContext.init(keyMangers, trustMangers, null)
Przykład gniazda - czytanie strony internetowej za pomocą prostego gniazda
import java.io.*;
import java.net.Socket;
public class Main {
public static void main(String[] args) throws IOException {//We don't handle Exceptions in this example
//Open a socket to stackoverflow.com, port 80
Socket socket = new Socket("stackoverflow.com",80);
//Prepare input, output stream before sending request
OutputStream outStream = socket.getOutputStream();
InputStream inStream = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
PrintWriter writer = new PrintWriter(new BufferedOutputStream(outStream));
//Send a basic HTTP header
writer.print("GET / HTTP/1.1\nHost:stackoverflow.com\n\n");
writer.flush();
//Read the response
System.out.println(readFully(reader));
//Close the socket
socket.close();
}
private static String readFully(Reader in) {
StringBuilder sb = new StringBuilder();
int BUFFER_SIZE=1024;
char[] buffer = new char[BUFFER_SIZE]; // or some other size,
int charsRead = 0;
while ( (charsRead = rd.read(buffer, 0, BUFFER_SIZE)) != -1) {
sb.append(buffer, 0, charsRead);
}
}
}
Powinieneś otrzymać odpowiedź zaczynającą się od HTTP/1.1 200 OK
, która wskazuje na normalną odpowiedź HTTP, następnie resztę nagłówka HTTP, a następnie nieprzetworzoną stronę internetową w formie HTML.
Należy zauważyć, że readFully()
jest ważna, aby zapobiec przedwczesnemu wyjątkowi EOF. W ostatnim wierszu strony internetowej może brakować readLine()
powrotu, sygnalizującego koniec wiersza, a następnie readLine()
będzie narzekać, więc należy go odczytać ręcznie lub skorzystać z metod narzędziowych z Apache commons-io IOUtils
Ten przykład ma na celu jedynie prostą demonstrację połączenia z istniejącym zasobem za pomocą gniazda, nie jest to praktyczny sposób uzyskiwania dostępu do stron internetowych. Aby uzyskać dostęp do strony internetowej za pomocą Java, najlepiej użyć istniejącej biblioteki klienta HTTP, takiej jak klient HTTP Apache lub klient HTTP Google
Podstawowa komunikacja klient / serwer za pomocą UDP (Datagram)
Client.java
import java.io.*;
import java.net.*;
public class Client{
public static void main(String [] args) throws IOException{
DatagramSocket clientSocket = new DatagramSocket();
InetAddress address = InetAddress.getByName(args[0]);
String ex = "Hello, World!";
byte[] buf = ex.getBytes();
DatagramPacket packet = new DatagramPacket(buf,buf.length, address, 4160);
clientSocket.send(packet);
}
}
W tym przypadku przekazujemy adres serwera za pomocą argumentu ( args[0]
). Port, którego używamy, to 4160.
Server.java
import java.io.*;
import java.net.*;
public class Server{
public static void main(String [] args) throws IOException{
DatagramSocket serverSocket = new DatagramSocket(4160);
byte[] rbuf = new byte[256];
DatagramPacket packet = new DatagramPacket(rbuf, rbuf.length);
serverSocket.receive(packet);
String response = new String(packet.getData());
System.out.println("Response: " + response);
}
}
Po stronie serwera zadeklaruj DatagramSocket na tym samym porcie, do którego wysłaliśmy naszą wiadomość (4160) i poczekaj na odpowiedź.
Multiemisja
Multiemisja jest rodzajem gniazda datagramu. W przeciwieństwie do zwykłych datagramów, multiemisja nie obsługuje każdego klienta indywidualnie, zamiast tego wysyła go na jeden adres IP, a wszyscy subskrybowani klienci otrzymują wiadomość.
Przykładowy kod po stronie serwera:
public class Server {
private DatagramSocket serverSocket;
private String ip;
private int port;
public Server(String ip, int port) throws SocketException, IOException{
this.ip = ip;
this.port = port;
// socket used to send
serverSocket = new DatagramSocket();
}
public void send() throws IOException{
// make datagram packet
byte[] message = ("Multicasting...").getBytes();
DatagramPacket packet = new DatagramPacket(message, message.length,
InetAddress.getByName(ip), port);
// send packet
serverSocket.send(packet);
}
public void close(){
serverSocket.close();
}
}
Przykładowy kod po stronie klienta:
public class Client {
private MulticastSocket socket;
public Client(String ip, int port) throws IOException {
// important that this is a multicast socket
socket = new MulticastSocket(port);
// join by ip
socket.joinGroup(InetAddress.getByName(ip));
}
public void printMessage() throws IOException{
// make datagram packet to recieve
byte[] message = new byte[256];
DatagramPacket packet = new DatagramPacket(message, message.length);
// recieve the packet
socket.receive(packet);
System.out.println(new String(packet.getData()));
}
public void close(){
socket.close();
}
}
Kod do uruchomienia serwera:
public static void main(String[] args) {
try {
final String ip = args[0];
final int port = Integer.parseInt(args[1]);
Server server = new Server(ip, port);
server.send();
server.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
Kod do uruchomienia klienta:
public static void main(String[] args) {
try {
final String ip = args[0];
final int port = Integer.parseInt(args[1]);
Client client = new Client(ip, port);
client.printMessage();
client.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
Najpierw uruchom klienta: Klient musi zasubskrybować adres IP, aby mógł zacząć odbierać jakiekolwiek pakiety. Jeśli uruchomisz serwer i wywołasz metodę send()
, a następnie printMessage()
klienta (& call printMessage()
). Nic się nie stanie, ponieważ klient nawiązał połączenie po wysłaniu wiadomości.
Tymczasowo wyłącz weryfikację SSL (do celów testowych)
Czasami w środowisku programowania lub testowania łańcuch certyfikatów SSL mógł nie zostać w pełni ustalony (jeszcze).
Aby kontynuować opracowywanie i testowanie, możesz programowo wyłączyć weryfikację SSL, instalując „zaufanego” menedżera zaufania:
try {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
// Install the all-trusting trust manager
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
Pobieranie pliku przy użyciu kanału
Jeśli plik już istnieje, zostanie zastąpiony!
String fileName = "file.zip"; // name of the file
String urlToGetFrom = "http://www.mywebsite.com/"; // URL to get it from
String pathToSaveTo = "C:\\Users\\user\\"; // where to put it
//If the file already exists, it will be overwritten!
//Opening OutputStream to the destination file
try (ReadableByteChannel rbc =
Channels.newChannel(new URL(urlToGetFrom + fileName).openStream()) ) {
try ( FileChannel channel =
new FileOutputStream(pathToSaveTo + fileName).getChannel(); ) {
channel.transferFrom(rbc, 0, Long.MAX_VALUE);
}
catch (FileNotFoundException e) { /* Output directory not found */ }
catch (IOException e) { /* File IO error */ }
}
catch (MalformedURLException e) { /* URL is malformed */ }
catch (IOException e) { /* IO error connecting to website */ }
Notatki
- Nie zostawiaj pustych bloków połowowych!
- W przypadku błędu sprawdź, czy plik zdalny istnieje
- Jest to operacja blokowania, przy dużych plikach może zająć dużo czasu