[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.
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.
Server program: Java Client program: JavaScript
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>
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.
Describe the action when connecting a socket
socket.onopen = function () {・ ・ ・};
If the socket connection is successful, this function is executed first.
socket.onmessage = function () {・ ・ ・};
This function is executed when data is sent from the server.Four. Describe the content to send a message
socket.send (...);
The sent content is sent back from the fictitious server as it is and displayed.
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);
}
}
}
Create an instance of the server socket.
ServerSocket variable = new ServerSocket (port number)
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" **
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.
var variable = new WebSocket ("ws: //127.0.0.1:60000 ");
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.
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);
}
}
}
Make each field an array to accommodate multiple clients. Initialize the array with the main function and edit the variables so far.
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.
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.
-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.
・ Port release If the port specified in the program is already in use, an error will occur when starting the server.
> netstat -ano
at the command prompt·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.
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.
-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