Socket communication with a web browser using Java and JavaScript ①

background

[Update]: Easy implementation using API

Described what was practiced about socket communication in order to create a multi-person simultaneous communication on the net. I want multiple real-time communications such as chat and online games.

I wanted to build a program in "Java" that can be executed in a "web browser", but it doesn't appear much even if I google it, so I will describe it. (Because I didn't know the word network programming due to lack of knowledge, there may be a problem with how to google ...)

Start from super basics such as reading and writing data.

Socket communication

A type of communication method. Real-time two-way communication is possible between the server and client instead of unidirectional communication like HTTP get access and post access. ⇒ You need two programs, a server program and a client program.

Please google for details.

Execution environment

language

Server program: Java Client program: JavaScript

Deliverables

  1. Chat app that runs on a web browser

Development procedure

  1. Build a client-side program
  2. Build a server-side program
  3. Access the server from the client program
  4. OK if there is a response from the server

Practice items

  1. Socket communication by client echo
  2. Single client and server chat app
  3. Multiple client and server chat app

1. Socket communication by client echo

The client-side program is written in JavaScript. It's almost perfect here. No server-side programs required.

<!DOCTYPE html>
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
		<title>WebSocket communication</title>
		<script type="text/javascript">
		
			var wSck= new WebSocket("echo.websocket.org");//WebSocket object generation
			
			wSck.onopen = function() {//Action when connecting socket
				document.getElementById('show').innerHTML += "Connected." + "<br/>";
			};
			
			wSck.onmessage = function(e) {//Action when receiving a message
				document.getElementById('show').innerHTML += e.data + "<br/>";
			};
			
			var sendMsg = function(val) {//Actions when sending a message
				var line = document.getElementById('msg');//Get input
				wSck.send(line.value);//Send to socket
				line.value = "";//Clear the contents
			};
		</script>
	</head>

	<body>
		<div
			style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;"
			id="show"></div>
		<input type="text" size="80" id="msg" name="msg" />
		<input type="button" value="Send" onclick="sendMsg();" />
	</body>
</html>

Code description

    1. Create a WebSocket object and assign an address to the argument. var variable = new WebSocket ("ws: // IP address: port number ");

This time, "echo.websocket.org" is used for the address. ⇒ It will return what was sent from the client as it is. Convenient because socket communication is possible without creating a server program.

  1. Describe the action when connecting a socket socket.onopen = function () {・ ・ ・}; If the socket connection is successful, this function is executed first.

    1. Describe the action when a message is received socket.onmessage = function () {・ ・ ・}; This function is executed when data is sent from the server.

Four. Describe the content to send a message socket.send (...);

Execution result

キャプチャ.JPG キャプチャ2.JPG キャプチャ3.JPG

The sent content is sent back from the fictitious server as it is and displayed.

2. Single client and server chat app

Instead of a fictitious server, actually create a server and reply as it is sent from the client. Write the server-side program in Java.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Encoder;

class SocketFree {

	private static ServerSocket sSck;//Socket for server
	private static Socket sck;//Reception socket
	private static InputStream is;//Input stream
	private static InputStreamReader isr;//Read the input stream
	private static BufferedReader in;//Reading text by buffering
	private static OutputStream os;//Output stream
	
	public static void main(String[] args) {
		try {
			sSck=new ServerSocket(60000);//Create an instance of a server socket(Port is 60000)
			System.out.println("I connected to the server!");
			sck=sSck.accept();//Waiting for connection. Substitute in the socket when it comes.
			System.out.println( "Participants have connected!");
			
			//Create the required I / O stream
			is=sck.getInputStream();//Read the input from the socket as a string of bytes
			isr=new InputStreamReader(is);//Convert the read byte string and read the character string
			in=new BufferedReader(isr);//Buffering strings(collect)Read
			os=sck.getOutputStream();//Write a string of bytes to the socket
			
			//Return connection permission to client(handshake)
			handShake(in , os);
			
			//Return the input to the socket as it is * Up to 125 characters(Header frame changes for 126 characters or more)
			echo(is , os);
			
		} catch (Exception e) {
			System.err.println("An error has occurred: " + e);
		}
	}
	
	//Method that returns connection permission to client(handshake)
	public static void handShake(BufferedReader in , OutputStream os){
		String header = "";//Variable declaration in header
		String key = "";//Variable declaration of websocket key
		try {
			while (!(header = in.readLine()).equals("")) {//Substitute the header obtained from the input stream into the string and loop all lines.
				System.out.println(header);//Show header contents on console line by line
				String[] spLine = header.split(":");//One line ":And put it in the array
				if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Key line
					key = spLine[1].trim();//Trim blanks and get websocket key
				}
			}
			key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//Add a mysterious string to the key
			byte[] keyUtf8=key.getBytes("UTF-8");//Key to "UTF-Convert to a byte array of "8"
			MessageDigest md = MessageDigest.getInstance("SHA-1");//Returns an object that implements the specified digest algorithm
			byte[] keySha1=md.digest(keyUtf8);//Key(UTF-8)Do a digest calculation using
			Encoder encoder = Base64.getEncoder();//Base64 encoder available
			byte[] keyBase64 = encoder.encode(keySha1);//Key(SHA-1)Is Base64 encoded
			String keyNext = new String(keyBase64);//Key(Base64)To String
			byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n"
		            + "Connection: Upgrade\r\n"
		            + "Upgrade: websocket\r\n"
		            + "Sec-WebSocket-Accept: "
		            + keyNext
		            + "\r\n\r\n")
		            .getBytes("UTF-8");//Create HTTP response
			os.write(response);//Send HTTP response
		} catch (IOException e) {
			System.err.println("An error has occurred: " + e);
		} catch (NoSuchAlgorithmException e) {
			System.err.println("An error has occurred: " + e);
		}
	}

	//A method that monitors the input to the socket in an infinite loop
	public static void echo(InputStream is , OutputStream os) {
		try{
			while(true) {
				byte[] buff = new byte[1024];//An array that contains the binary data sent by the client
				int lineData =is.read(buff);//Read data
				for (int i = 0; i < lineData - 6; i++) {
					buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//3 after the 7th byte-Decode using the 6th byte key
				}
				String line = new String(buff, 6, lineData - 6, "UTF-8");//Convert the decoded data to a character string
				byte[] sendHead = new byte[2];//Prepare header to send back
				sendHead[0] = buff[0];//The first byte is the same
				sendHead[1] = (byte) line.getBytes("UTF-8").length;//The second byte is the length of the string
				os.write(sendHead);//Header output
				os.write(line.getBytes("UTF-8"));//Converts the character string to binary data after the 3rd byte and outputs it
				
				if (line.equals("bye")) break;//If "bye" is sent, reception ends
			}
		} catch (Exception e)  {
			System.err.println("An error has occurred: " + e);
		}
	}
}

Code description

    1. Declare necessary variables such as sockets and streams.
  1. Create an instance of the server socket. ServerSocket variable = new ServerSocket (port number)

    1. Wait for a connection from the client with the accept method.

Four. Create an I / O stream for the connected client.

Five. Returns a response to a connection request from a client. (handShake method)

Since the required information is described in the request header, edit it and send it back. For details of the edited contents, refer to the code comment and reference site. In short, you need to send back the "WebSocket key" in the request header. ⇒ ** "Handshake" **

  1. After connecting to the socket, the input from the client is returned as it is in an infinite loop. (echo method)

The data sent by the client is encoded as binary data. Decode on the server side, re-encode and send to the client. For details of the decoded contents, refer to the code comment and reference site.

    1. When "bye" is sent from the client, it exits the loop and ends reception.

Execution result

  1. Start the server program
  2. Specify the port number and connect to localhost

キャプチャ3.JPG

I got the same result as when I used a fictitious server (echo.websocket.org). This time it's just echoing, but it's also possible to play around with the data on the server side and reply.

Multiple client and server chat app

Edit the server program to allow connections from multiple clients. In addition, real-time communication will be provided between each client.


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Base64.Encoder;

class SocketFree {

	private static ServerSocket sSck;//Socket for server
	private static Socket[] sck;//Reception socket
	private static InputStream[] is;//Input stream
	private static InputStreamReader[] isr;//Read the input stream
	private static BufferedReader[] in;//Reading text by buffering
	private static OutputStream[] os;//Output stream
	private static ClientThread user[];//Instance of each client
	private static int member;//Number of connected members
	
	public static void main(String[] args) {
		int n=0;
		int maxUser=100;
		//Prepare an array of each field
		sck=new Socket[maxUser];
		is=new InputStream[maxUser];
		isr=new InputStreamReader[maxUser];
		in=new BufferedReader[maxUser];
		os=new OutputStream[maxUser];
		user=new ClientThread[maxUser];
				
		try {
				sSck=new ServerSocket(60000);//Create an instance of a server socket(Port is 60000)
				System.out.println("I connected to the server!");
				while(true) {
					sck[n]=sSck.accept();//Waiting for connection. Substitute in the socket when it comes.
					System.out.println( (n+1)+"The second participant has connected!");
					
					//Create the required I / O stream
					is[n]=sck[n].getInputStream();//Read the input from the socket as a string of bytes
					isr[n]=new InputStreamReader(is[n]);//Convert the read byte string and read the character string
					in[n]=new BufferedReader(isr[n]);//Buffering strings(collect)Read
					os[n]=sck[n].getOutputStream();//Write a string of bytes to the socket
					
					//Return connection permission to client(handshake)
					handShake(in[n] , os[n]);
					
					//Create a thread for each client
					user[n] = new ClientThread(n , sck[n] , is[n] , isr[n] , in[n] , os[n]);
					user[n].start();
					
					member=n+1;//Update the number of connections
					n++;//To the next connecter
				}
			} catch (Exception e) {
				System.err.println("An error has occurred: " + e);
		}
	}
	
	//Method that returns connection permission to client(handshake)
	public static void handShake(BufferedReader in , OutputStream os){
		String header = "";//Variable declaration in header
		String key = "";//Variable declaration of websocket key
		try {
			while (!(header = in.readLine()).equals("")) {//Substitute the header obtained from the input stream into the string and loop all lines.
				System.out.println(header);//Show header contents on console line by line
				String[] spLine = header.split(":");//One line ":And put it in the array
				if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Key line
					key = spLine[1].trim();//Trim blanks and get websocket key
				}
			}
			key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//Add a mysterious string to the key
			byte[] keyUtf8=key.getBytes("UTF-8");//Key to "UTF-Convert to a byte array of "8"
			MessageDigest md = MessageDigest.getInstance("SHA-1");//Returns an object that implements the specified digest algorithm
			byte[] keySha1=md.digest(keyUtf8);//Key(UTF-8)Do a digest calculation using
			Encoder encoder = Base64.getEncoder();//Base64 encoder available
			byte[] keyBase64 = encoder.encode(keySha1);//Key(SHA-1)Is Base64 encoded
			String keyNext = new String(keyBase64);//Key(Base64)To String
			byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n"
		            + "Connection: Upgrade\r\n"
		            + "Upgrade: websocket\r\n"
		            + "Sec-WebSocket-Accept: "
		            + keyNext
		            + "\r\n\r\n")
		            .getBytes("UTF-8");//Create HTTP response
			os.write(response);//Send HTTP response
		} catch (IOException e) {
			System.err.println("An error has occurred: " + e);
		} catch (NoSuchAlgorithmException e) {
			System.err.println("An error has occurred: " + e);
		}
	}
	
	//A method that sends data from each client to all clients
	public static void sendAll(int number , byte[] sendHead , String line){
		try {
			for (int i = 0; i <member ; i++) {
				os[i].write(sendHead);//Header output
				os[i].write(line.getBytes("UTF-8"));//Converts the character string to binary data after the 3rd byte and outputs it
				System.out.println((i+1)+"Second"+(number+1)+"I sent the second message!" );
			}
		} catch (IOException e) {
			System.err.println("An error has occurred: " + e);
		}
	}
}

class ClientThread extends Thread{
	//Fields for each client
	private int myNumber;
	private Socket mySck;
	private InputStream myIs;
	private InputStreamReader myIsr;
	private BufferedReader myIn;
	private OutputStream myOs;
	
	//Assign each value to the field of the instance in the constructor
	public ClientThread(int n , Socket sck , InputStream is , InputStreamReader isr , BufferedReader in , OutputStream os) {
		myNumber=n;
		mySck=sck;
		myIs=is;
		myIsr=isr;
		myIn=in;
		myOs=os;
	}
	
	//Thread class main
	public void run() {
		try {
			echo(myIs , myOs , myNumber);
		} catch (Exception e) {
			System.err.println("An error has occurred: " + e);
		}
	}
	
	//Monitor the input to the socket in an infinite loop * Up to 125 characters(Header frame changes for 126 characters or more)
	public void echo(InputStream is , OutputStream os , int myNumber) {
		try{
			while(true) {
				byte[] buff = new byte[1024];//An array that contains the binary data sent by the client
				int lineData =is.read(buff);//Read data
				for (int i = 0; i < lineData - 6; i++) {
					buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//3 after the 7th byte-Decode using the 6th byte key
				}
				String line = new String(buff, 6, lineData - 6, "UTF-8");//Convert the decoded data to a character string
				byte[] sendHead = new byte[2];//Prepare header to send back
				sendHead[0] = buff[0];//The first byte is the same
				sendHead[1] = (byte) line.getBytes("UTF-8").length;//The second byte is the length of the string
				
				SocketFree.sendAll(myNumber , sendHead , line);//Send to each client is executed by sedAll method of original class
				
				if (line.equals("bye")) break;//If "bye" is sent, reception ends
			}
		} catch (Exception e)  {
			System.err.println("An error has occurred: " + e);
		}
	}
}

Code description

    1. Added field variables to keep track of client numbering and the number of connections.
  1. Make each field an array to accommodate multiple clients. Initialize the array with the main function and edit the variables so far.

    1. Create ClientThread class that inherits Thread class to process multiple clients in parallel. Process up to the handshake in the main function. After that, create a ClientThread instance for each client and process in parallel.

Thread class: Can be branched from the main function and processed at the same time. A mysterious specification where run () is executed by .start () ;.

Four. Moved the echo function to the ClientThread class. Data reception ⇒ Decoding ⇒ Data transmission Since data transmission is executed in the class to which the main function belongs, a new sendAll method is created.

Five. Other minor corrections such as method types and variable names.

Execution result

  1. Start the server program
  2. Connect to localhost from multiple browsers

キャプチャ5.JPG

When sent from one client, the same text is displayed in all browsers. The display result is the same regardless of which client sends the text.

Other

Improvement points

-Error handling when the browser is closed If you send "bye", you can exit the monitoring loop and exit without error, but if you close the browser during monitoring, an error will occur and the server will stop.

-Addition of close method Originally, when terminating socket communication, the close method is used to disconnect, but this time it is not used.

・ Enhancement of decoding process Since the decoding process has been spoiled, communication of 125 characters or less is possible. Since the frame of the web socket changes when it becomes 126 characters or more, it is necessary to classify the cases.

・ Strengthening security Since no measures have been taken this time and the security is loose, it is necessary to think about the method of sending and receiving.

Remarks

・ Port release If the port specified in the program is already in use, an error will occur when starting the server.

  1. Run > netstat -ano at the command prompt
  2. Confirm PID from the specified local address
  3. Check the PDI application from the task manager process
  4. If you don't need it, end the process and open the port キャプチャ6.JPG

·handshake An agreement must be reached between the client and server before socket communication can take place. If there is a connection request from the client to the server, it needs to respond to it. The content of the response is described in detail on the reference page.

・ Decoding of received data There is a frame format for the data in the web socket, and binary data is transmitted along that frame. To decode the data, it is necessary to understand the frame structure and convert it byte by byte. Details of the frame format and decoding method are described in detail on the reference page.

Impressions

It's more complicated than I expected.

Even if I google it, it doesn't come out easily, is it a minor method? I often see server programs written in "Node.js". The dot installation video was created with Node.js, and complicated processing such as response and decoding was not performed. Maybe it's faster to study Node.js.

Even in Java, I saw an article that allows communication without decoding by using annotations, but this time it passed. It might have been better not to pass.

I should have been studying programming, but before I knew it, I was studying protocols ... I didn't think it was a place for beginners to poke their necks, but it was good because it was a learning experience.

If you make a mistake, Sman. If you notice it, fix it. There are quite a lot of things to improve, so I'll update if possible.

Reference page

-Site that I managed to manage -Site that is easy to understand for multiple clients -Detailed site 1 about response headers and decoding -Detailed site 2 about response headers and decoding -Sites that are familiar with websockets in general -WebSockets in general + Sites using annotations -Commentary: A site that creates a few details, but quite fine details -Create client program with JavaScript

Recommended Posts

Socket communication with a web browser using Java and JavaScript ②
Socket communication with a web browser using Java and JavaScript ①
Create a high-performance enum with fields and methods like Java with JavaScript
Let's try WebSocket with Java and javascript!
Graph the sensor information of Raspberry Pi in Java and check it with a web browser
Prepare a scraping environment with Docker and Java
Create a portfolio app using Java and Spring Boot
How to convert A to a and a to A using AND and OR in Java
Create a JAVA WEB application and try OMC APM
[Java] Development with multiple files using package and import
[Java] JSON communication with jackson
I want to make a list with kotlin and java!
I want to make a function with kotlin and java!
Using Mapper with Java (Spring)
Problems with Dijkstra's algorithm using PriorityQueue and adjacency list (java)
Try developing a containerized Java web application with Eclipse + Codewind
Create a Java and JavaScript team development environment (gradle environment construction)
A Simple CRUD Sample Using Java Servlet / JSP and MySQL
Make a rhombus using Java
AWS Elastic Beanstalk # 1 with Java starting from scratch-Building a Java web application environment using the EB CLI-
Let's make a LAN communication application Part 1 New project creation using Maven and Java entry point
Create a simple web server with the Java standard library com.sun.net.httpserver
Comparison of WEB application development with Rails and Java Servlet + JSP
Automatically deploy a Web application developed in Java using Jenkins [Preparation]
Memorandum No.2 "Making a search history with ArrayList and HashSet" [Java]
Build a Java project with Gradle
Distributed tracing with OpenCensus and Java
Install Java and Tomcat with Ansible
Use JDBC with Java and Scala.
Try using Redis with Java (jar)
Build a web application with Javalin
I tried UDP communication with Java
Create a Java project using Eclipse
Output PDF and TIFF with Java 8
Using Java with AWS Lambda-Eclipse Preparation
Html5 development with Java using TeaVM
Try bidirectional communication with gRPC Java
Using proxy service with Java crawling
Encrypt with Java and decrypt with C #
Process Communication using AMQP with RabbitMQ
How to operate IGV using socket communication, and the story of making a Ruby Gem using that method
Build a web application development environment that uses Java, MySQL, and Redis with Docker CE for Windows
A collection of phrases that impresses the "different feeling" of Java and JavaScript
Using Java with AWS Lambda-Implementation Tips-Get Instance Name from Reagion and Instance ID
[Java] Create a jar file with both compressed and uncompressed with the jar command
Creating a java web application development environment with docker for mac part1
Java beginner tried to make a simple web application using Spring Boot
Automatically deploy a web application developed in Java using Jenkins [Spring Boot application]
I want to create a dark web SNS with Jakarta EE 8 with Java 11
[Java] Deploy a web application created with Eclipse + Maven + Ontology on Heroku
<java> Split the address before and after the street address with a regular expression
Create a java web application development environment with docker for mac part2
How to make an app with a plugin mechanism [C # and Java]
[Java] I installed JDBC and tried to connect with servlet + MySQL. (There is a version using DAO / Bean)
[Java] Create and apply a slide master
Create a web environment quickly using Docker
Try using Java framework Nablarch [Web application]
A look at Jenkins, OpenJDK 8 and Java 11
Using Java with AWS Lambda-Implementation-Check CloudWatch Arguments
4. Creating a manifest and running a web module
Monitor Java applications with jolokia and hawtio