Ce que j'ai appris lors de la création d'un serveur en Java

Le texte commence par [ici](# texte), veuillez donc le sauter à moins que vous ne soyez membre du comité ONCT ICT.

Excusez-moi, mais je ne suis toujours pas familier avec les serveurs et les réseaux ~~ (étude) ~~ Je ne suis pas familier avec cela, alors veuillez le signaler aux professionnels.

De côté

Pourquoi j'ai écrit cet article

・ On m'a demandé d'écrire un article sur exactement le même contenu ・ Je ne pouvais pas tant enseigner à mes juniors dans les conférences pendant les longues heures de vacances. ・ Recommandé par le président De. Surtout dans le département, je n'ai pas beaucoup de réalisations et je n'ai pas beaucoup d'éducation, alors j'ai pensé que je devais travailler un peu ici.

Dans les TIC, notamment avec le mot «Demandez à mes seniors», j'ai été obligé de construire soudainement un serveur SNS (sans même connaître l'adresse IP, encore moins AWS), j'ai donc pensé qu'il était nécessaire de partager des connaissances. (Je suis désolé si vous ne savez pas que c'est partagé)

Après cela, je veux diffuser encore plus Java, donc j'espère que vous l'utiliserez sur votre premier serveur.

Je suis presque autodidacte et je ne peux pas enseigner de connaissances de base, théoriques ou systématiques, mais au moins j'aimerais pouvoir vous montrer suffisamment de code pour exécuter un simple SNS et DCG.

Pour être honnête, ni le design ni le code ne sont utiles, mais j'espère qu'il pourra être utilisé pour sortir de la situation où les débutants ne connaissent pas les termes de google et ne connaissent pas la syntaxe minimale.

Texte

Au début, je l'ai écrit avec enthousiasme, mais quand j'ai fini d'écrire le aparté, je ne pouvais pas voir la fin, et ma motivation a diminué, alors je l'ai écrit grossièrement. Les plaintes sont acceptées dans les commentaires. De plus, en ce qui concerne SQL, cette fois, il était difficile d'écrire ~~, donc je l'ai omis ~~.

Avant de commencer la configuration

Que devons-nous faire du développement de serveurs en premier lieu?

En gros, écrivez un programme qui reçoit une chaîne de caractères du client (côté exploitation (HTML, application Android, etc.)) et renvoie le résultat du traitement sous forme de chaîne de caractères.

Je n'ai écrit que des programmes Java pour consoles?

Il existe une API pour le développement de serveurs appelée JavaEE. Si vous utilisez un conteneur Web tel que Tomcat, vous pouvez le publier et interagir avec d'autres programmes (ou ordinateurs).

J'ai entendu dire qu'il existe différentes méthodes de communication

Cette fois, je vais expliquer HTTP et WebSocket (WS). HTTP retourne essentiellement le traitement lorsqu'un message est reçu. (La communication est interrompue) WS conserve la connexion une fois connecté et s'envoie des messages. (La communication n'est pas interrompue)

Quel genre de programme avez-vous écrit spécifiquement?

Les deux projets que j'ai expérimentés sont les suivants.

SNS (serveur HTTP)

-Lorsque vous appuyez sur le bouton de publication sur le client (application Android), le contenu (ID, contenu de publication, informations de localisation, etc.) sera envoyé au serveur, alors enregistrez-le dans SQL. -Lorsque vous appuyez sur le bouton de lecture sur le client, un message indiquant la lecture sera envoyé. -Le serveur lit le message que l'utilisateur peut afficher à partir de SQL et le renvoie.

DCG (serveur WebSocket)

-Lorsque le client démarre, il se connecte d'abord au serveur. -Les opérations effectuées par le client sont envoyées au serveur une par une. -Le serveur dispose d'un programme de jeu et exploite le jeu en fonction des données envoyées. -Lorsque l'opération est terminée, le résultat du traitement est envoyé au client et le client dessine en fonction de celui-ci.

Cadre environnemental

Tout d'abord, l'environnement est le suivant.

Logiciel(Tel)
langage de développement Java (8 recommandé)
API de servlet JavaEE
IDE IntelliJ Community
Conteneur Web Tomcat
Outil de création Gradle

J'ai écrit à propos de ces configurations dans un article séparé, alors volez-y. (Je suis désolé)

Créer un fichier programme

Cliquez avec le bouton droit sur src-> main-> java et cliquez sur Nouveau-> Package pour créer le package. Cliquez ensuite avec le bouton droit de la souris sur le bas du package et sélectionnez Nouveau-> Fichier Java pour créer un fichier .java.

Essaye le

Comme mentionné dans l'article Préférences, la communauté IntelliJ n'a pas de fonctionnalités liées au démarrage du serveur, nous allons donc remplacer la tâche de Gradle pour l'exécution. Cliquez sur Gradle à l'extrême droite de l'écran pour afficher une liste de tâches. Je pense qu'il y a un élément appelé Tomcat Run dans gretty. Vous pouvez démarrer le serveur en double-cliquant dessus.

org.apache.catalina.LifecycleException: Protocol handler initialization failed
	at org.apache.catalina.connector.Connector.initInternal(Connector.java:935)
	at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
	at org.apache.catalina.core.StandardService.initInternal(StandardService.java:530)
	at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
	at org.apache.catalina.core.StandardServer.initInternal(StandardServer.java:852)
	at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:173)
	at org.apache.catalina.startup.Tomcat.start(Tomcat.java:371)
	at org.apache.catalina.startup.Tomcat$start$0.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
	at org.akhikhl.gretty.TomcatServerManager.startServer(TomcatServerManager.groovy:49)
	at org.akhikhl.gretty.ServerManager$startServer$0.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:128)
	at org.akhikhl.gretty.Runner.run(Runner.groovy:117)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:71)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120)
	at org.akhikhl.gretty.Runner.main(Runner.groovy:44)
Caused by: java.net.BindException: Address already in use: bind
	at sun.nio.ch.Net.bind0(Native Method)
	at sun.nio.ch.Net.bind(Net.java:433)
	at sun.nio.ch.Net.bind(Net.java:425)
	at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
	at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
	at org.apache.tomcat.util.net.NioEndpoint.initServerSocket(NioEndpoint.java:227)
	at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:202)
	at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:1043)
	at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:540)
	at org.apache.coyote.http11.AbstractHttp11Protocol.init(AbstractHttp11Protocol.java:74)
	at org.apache.catalina.connector.Connector.initInternal(Connector.java:932)
	... 27 more

http://localhost:8080/ Et accédons ici スクリーンショット (75).png Si Not Found est affiché comme ceci, cela réussit. Je n'ai encore écrit aucun programme, c'est donc naturel. En passant, si vous ne l'exécutez pas, une erreur de chargement de page etc. sera affichée.

Tout d'abord, créez un servlet Http

Un servlet est comme le programme principal d'un serveur (approprié) Ici, nous allons introduire Http avant WS.

Il existe une méthode en Http, qui est utilisée correctement en fonction du contenu de la communication, mais ici seul GET est utilisé pour simplifier. (Pardon) GET est une méthode qui demande des ressources, etc. au serveur. Il se présente sous la forme de transmission de traitement et de données à l'aide d'URL et de paramètres, et de réception des résultats d'exécution.

Créons d'abord un programme de servlet avec HttpServlet.java. (Le nom de la classe est gratuit)

Dans Tomcat, c'est une bonne pratique d'écrire un fichier de paramètres appelé web.xml, mais il semble que les annotations puissent être utilisées à la place, donc cette fois je le ferai.

Le contenu est affiché dans le code ci-dessous.

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.annotation.WebServlet;

//Obligatoire
//Transmettez le nom du servlet dans name et une liste d'URL qui accèdent à urlPatterns.
//Vous pouvez également spécifier un caractère générique.
@WebServlet(
        name = "HttpServlet",
        urlPatterns = {"/*"})
@MultipartConfig (
	     fileSizeThreshold= 32768 ,
	     maxFileSize= 5242880 ,
	     maxRequestSize= 27262976
	 )
// javax.servlet.http.Peut être utilisé comme servlet en héritant de HttpServlet
public class HttpServlet extends javax.servlet.http.HttpServlet {

	//La méthode qui est appelée lorsque la méthode doGet reçoit une requête GET
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response){
		PrintWriter out = null;
		try {
			//Cette zone est magique pour le moment.
			//Code de caractère et ainsi de suite
			response.setContentType("text/html; charset=UTF-8");
	        response.setHeader("Access-Control-Allow-Origin", "*");
			request.setCharacterEncoding("utf-8");

			//En obtenant PrintWiter, System.out.println()Vous pouvez renvoyer une chaîne de caractères comme celle-ci.
			out = response.getWriter();

			// request.getServletPath();Reçoit le chemin entré ci-dessous à partir de la racine de ce projet.
			//Cette fois, branchez de manière appropriée avec un interrupteur.
			String path = request.getServletPath();
			String str = null;
			switch(path){
				case "/hoge":
				// request.getParameter()Recevez les paramètres à.
				// http://localhost:8080/Test/hoge?str=hage&str2=hige
				//URL comme?str=hage&str2=partie hige
				str = request.getParameter("str");
				break;
				case "/huga":
				str = "huga";
				break;
			}
			//Renvoyez le contenu de str.
			out.println(str);
			//Appelez également flush correctement.
			out.flush();

		} catch (IOException e){
			e.printStackTrace();
		} 
	}


}

Si vous faites cela et marchez sur une URL comme celle ci-dessous, vous reviendrez aaa. [http: // localhost: 8080 / / hoge? Str = aaa](http: // localhost: 8080 / Project / hoge? Str = aaa) En utilisant cela, le programme à exécuter est branché, les données sont reçues et le traitement est effectué. ~~ Je l'ai utilisé ~~ Il semble que le paramètre request n'ait qu'un problème de sécurité, alors ne l'utilisez pas trop en pratique. Aussi, ici j'ai écrit le processus directement dans le cas, mais je pense qu'il vaut mieux le séparer en fonctions.

Ensuite, écrivons un servlet WebSocket

Cette fois, j'écrirai un servlet qui utilise WebSocket au lieu de Http. Http était une méthode qui répond à la communication du client (de base) (établit une nouvelle connexion à chaque fois), WebSocket maintient la communication avec le client et communique dans les deux sens.

package scptcg.server;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;

//Avec cela, il devient un serveur WebSocket
//L'URL peut être un caractère générique
@ServerEndpoint("/ws")
public final class WSEndPoint {

    // Session(la communication)Carte à enregistrer
    private static Map<String, Session> session = new HashMap<>();

    //Méthode appelée lors de la connexion
    @OnOpen
    public void onOpen(final Session client, final EndpointConfig config) {
        String log = client.getId() + " was connected.";
        System.out.println(log);
    }

    //Méthode appelée lors de la déconnexion
    @OnClose
    public void onClose(final Session client, final CloseReason reason) throws IOException {
        String log = client.getId() + " was closed by "
                + reason.getCloseCode() + "[" + reason.getCloseCode().getCode() + "]";
        System.out.println(log);
    }

    //Méthode appelée en cas d'erreur
    @OnError
    public void onError(final Session client, final Throwable error) {
        String log = client.getId() + " was error. [" + error.getMessage() + "]";
        error.printStackTrace();
    }

    //Méthode appelée lors de l'envoi d'un message
    @OnMessage
    public void onMessage(final String text, final Session client) throws IOException {
        //On suppose que le contenu du message décrit l'opération, l'identifiant et les données séparés par des sauts de ligne.
        String[] t = text.split("\n");
        String event = t[0];
        String id = t[1];
        //Branche pour chaque contenu d'événement
        switch (event){
            case "login":
                //Enregistrez la session dans HashMap.
                session.put(id, client);
                //Envoyez une chaîne à la session enregistrée avec l'ID.
                session.get(id).getBasicRemote().sendText(id);
                System.out.println(id);
                break;
            case "commit":
                //diffuser
                for (Session s :session.values()){
                    s.getBasicRemote().sendText(t[2]);
                }
                System.out.println(t[2]);
                break;
            case "close":
                //Supprimer de la liste des sessions
                session.remove(t[1]);
                break;
        }

    }

}

Cela se fait dans la méthode OnMessage (). Il semble qu'il n'y ait pas de branche du contenu de traitement à l'origine, donc je pense qu'il est préférable de le transmettre au début du contenu de communication. Au fait, puisqu'il s'agit d'un échantillon, il s'agit de ~~ putain de ~~ données séparées par des sauts de ligne, mais j'utilise généralement Gson pour échanger JSON. Les annotations telles que @OnMessage reconnaissent la méthode appelée, il n'est donc pas nécessaire d'hériter d'une classe spéciale. ~~ C'est gênant, je vais donc omettre Gson également.

Je mettrai également le HTML pour le test. Cependant, je ne l'ai pas testé, donc je pense qu'il vaut mieux l'écrire vous-même.

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Sample</title>
    <script type="text/javascript">
        var uri = "ws://localhost:8080/Project/";

        //Objet WebSocket
        var webSocket = null;

        //Traitement initial
        function init() {
            if (webSocket == null) {
                //Initialisation WebSocket
                webSocket = new WebSocket(uri);
                //Paramètres du gestionnaire d'événements
                webSocket.onopen = onOpen;
                webSocket.onmessage = onMessage;
                webSocket.onclose = onClose;
                webSocket.onerror = onError;
				webSocket.send("login\n" + document.getElementById('id').innerText);
            }
        }

        function onOpen(event) {
			alert('open');
        }

        //Événement de réception de message
        function onMessage(event) {
            document.getElementById('box').innerText += event;
        }

        //Événement d'erreur
        function onError(event) {
			alert('error');
        }

        //Événement de déconnexion
        function onClose(event) {
			alert('close');
            webSocket = null;
        }

		function send(){
			webSocket.send("commit\n" + document.getElementById('txt').innerText);
		}

    </script>
</head>
<body>
	<input type="id" name="name" size="30" maxlength="20" />
	<input type="txt" name="name" size="30" maxlength="20" />
	<hr />
    <input id="box" type="text" data-name="message" size="100" />
	<hr />
	<button id="con" onclick="init()">init</button>
	<button id="send" onclick="send()">send</button>
</body>
</html>

À propos, l'URL est [ws: // localhost: 8080 / /](ws: // localhost: 8080 / Project /).

en conclusion

Je suis désolé, je suis en désordre Je vais mettre le code SNS et DCG sur mon GitHub, alors jetez un œil. ~~ (Je suis surpris de la putain de code) ~~

Je l'ai écrit sérieusement, donc si vous avez une demande de correction ou d'explication supplémentaire, dites-le directement ou sur SNS ~~ et écrivez-le dans le commentaire.

Passez un bon Noël à tous!

Recommended Posts

Ce que j'ai appris lors de la création d'un serveur en Java
[Note] Ce que j'ai appris en six mois grâce à des inexpérimentés (Java)
[Note] Ce que j'ai appris en six mois grâce à des inexpérimentés (Java) (1)
[Note] Ce que j'ai appris en six mois grâce à des inexpérimentés (Java) (3)
Ce que j'ai appris en Java (partie 2) Que sont les variables?
J'ai créé un PDF avec Java.
Ce que j'ai appris avec Java Gold
Ce que j'ai appris avec Java Silver
Ce que j'ai appris en Java (partie 4) Branchement conditionnel et répétition
[java] Ce que j'ai fait en comparant des listes dans ma propre classe
Qu'est-ce qu'une classe en langage Java (3 /?)
Lors de la recherche de plusieurs dans un tableau Java
Ce que j'ai appris en Java (partie 1) Flux de développement Java et présentation
À propos de ce que j'ai fait lors de la création d'un fichier .clj avec Clojure
Qu'est-ce qu'une classe en langage Java (1 /?)
Qu'est-ce qu'une classe en langage Java (2 /?)
Ce que j'ai appris
Ce que j'ai appris du calcul monétaire Java
[Rails] Ce que j'ai appris d'une petite pierre d'achoppement lors de l'utilisation de l'ascendance
J'ai créé un programme de jugement des nombres premiers en Java
Une note quand vous voulez Tuple en Java
Je voulais que (a == 1 && a == 2 && a == 3) vrai en Java
J'ai écrit un programme de jugement des nombres premiers en Java
J'ai créé un jeu Janken en Java (CLI)
Un examen rapide de Java appris en classe
Ce que j'ai fait lorsque j'ai converti Java en Kotlin
J'ai écrit un programme de factorisation prime en Java
Résumé de ce que j'ai appris dans Spring Batch
Ce que je n'aime pas lors de l'utilisation de l'interface d'une fonction avec des arguments par défaut dans Kotlin depuis Java
Ce que j'ai appris ② ~ Mock ~
Ce que j'ai appris ① ~ DJUnit ~
J'ai fait un jeu de problèmes de calcul simple en Java
Un examen rapide de Java appris en classe part4
J'ai essayé de créer une compétence Clova en Java
J'ai essayé de créer une fonction de connexion avec Java
J'ai essayé d'utiliser Log4j2 sur un serveur Java EE
J'ai créé un serveur et un client Restful au printemps.
Un examen rapide de Java appris en classe part3
Un examen rapide de Java appris en classe part2
J'ai essayé de découvrir ce qui avait changé dans Java 9
Ce que j'ai recherché sur Java 8
Ce que j'ai recherché sur Java 6
J'ai fait une roulette à Java.
Ce que j'ai recherché sur Java 9
Rechercher un sous-ensemble en Java
Ce que j'ai recherché sur Java 7
J'ai essayé la métaprogrammation avec Java
Ce que j'ai appris sur Kotlin
Ce que j'ai recherché sur Java 5
Mettez à jour vos connaissances Java en écrivant un serveur gRPC en Java (2)
Résoudre l'erreur CreateProcess = 206 lors de l'exécution de Java dans un environnement Windows
Mettez à jour vos connaissances Java en écrivant un serveur gRPC en Java (1)
Je voulais juste créer une propriété réactive en Java
Même en Java, je veux afficher true avec un == 1 && a == 2 && a == 3
J'ai essayé de convertir une chaîne de caractères en un type LocalDate en Java
Comment changer dynamiquement les JDK lors de la construction de Java avec Gradle
Afficher une erreur détaillée dans Logger lors de l'exécution de Java sur le serveur
J'ai créé un client RESAS-API en Java