Socket Makes a LAN Chat Tool

Posted by dcro2 on Tue, 30 Jul 2019 05:21:15 +0200

Socket Makes a LAN Chat Tool

First create a serializable stream class SerialiableMessage
This serialized message defines the transmission message structure. There are five member variables, which record the following information separately:
Private String sendUser Nmae; -- Sender username
 private String sendIp; -- sender ip
 Private String ReciveUserName; -- Receiver username
 Private String ReciveIp; Receiver ip
 private String message; -- message content

The code is as follows:

package com.Test.com;

import java.io.Serializable;

public class SerialiableMessage implements Serializable {
private String sendUserNmae;
private String sendIp;
private String receiveUserName;
private String receiveIp;
private String message;

public SerialiableMessage(String sendUserNmae, String sendIp, String receiveUserName, String message) {
    this.sendUserNmae = sendUserNmae;
    this.sendIp = sendIp;
    this.receiveUserName = receiveUserName;
    this.message = message;
}

public String getSendUserNmae() {
    return sendUserNmae;
}

public void setSendUserNmae(String sendUserNmae) {
    this.sendUserNmae = sendUserNmae;
}

public String getSendIp() {
    return sendIp;
}

public void setSendIp(String sendIp) {
    this.sendIp = sendIp;
}

public String getReceiveUserName() {
    return receiveUserName;
}

public void setReceiveUserName(String receiveUserName) {
    this.receiveUserName = receiveUserName;
}

public String getReceiveIp() {
    return receiveIp;
}

public void setReceiveIp(String receiveIp) {
    this.receiveIp = receiveIp;
}

public String getMessage() {
    return message;
}

public void setMessage(String message) {
    this.message = message;
}

public SerialiableMessage() {
}

public SerialiableMessage(String sendUserNmae, String sendIp, String receiveUserName, String receiveIp, String message) {
    this.sendUserNmae = sendUserNmae;
    this.sendIp = sendIp;
    this.receiveUserName = receiveUserName;
    this.receiveIp = receiveIp;
    this.message = message;
}

}

Next, define the Client class

Include the client main method, get the server connection < ip request connection broadcasted by the receiving server >, print the list of friends < I add a few users arbitrarily in the list >, start the thread and so on.
package com.Test.com;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Client {

static HashMap<String, String> friendMap = new HashMap<>();
public static void main(String[] args) throws IOException, InterruptedException {
    Scanner sc=new Scanner(System.in);
    while (true){
        client();
        System.out.println("Do you want to continue??[Y/N]");
        String s = sc.nextLine();
        if (s.equals("N")) {
            break;
        }
    }
}

public static void client() throws IOException, InterruptedException {
    ArrayList<Boolean>threadState=new ArrayList<Boolean>();
    threadState.add(true);
    Scanner sc = new Scanner(System.in);
    DatagramSocket serveMachineIpdatagramSocket=new DatagramSocket(5050);
    byte[]bys=new byte[1024];
    DatagramPacket serveMachineIpdatagramPacket=new DatagramPacket(bys,bys.length);
    System.out.println("Waiting for server response...");
    serveMachineIpdatagramSocket.receive(serveMachineIpdatagramPacket);
    String serveMachineIp = new String(bys, 0, bys.length);
    Socket socket = new Socket(serveMachineIp, 8000);
    printFriendMap();
    System.out.println("Please choose friends to chat with.");
    String receiveUserName = sc.next();
    String receiveIp = friendMap.get(receiveUserName);
    ClientSendRunnable clientSendRunnable = new ClientSendRunnable(socket, receiveUserName, receiveIp,threadState);
    ClientReceiveRunnable clientReceiveRunnable = new ClientReceiveRunnable(socket, friendMap,threadState);
    System.out.println("Start chatting!");
    creatChat(clientSendRunnable, clientReceiveRunnable);
}


public static void creatChat(ClientSendRunnable clientSendRunnable, ClientReceiveRunnable clientReceiveRunnable) throws InterruptedException {
    Thread clientSendThread = new Thread(clientSendRunnable);
    clientSendThread.start();
    Thread clientReceiveThread = new Thread(clientReceiveRunnable);
     clientReceiveThread.start();
     clientSendThread.join();
     clientReceiveThread.join();
}

public static void printFriendMap() {
    friendMap.put("ewew", "192.168.13.45");
    friendMap.put("eatr", "192.168.13.45");
    friendMap.put("uiik", "192.168.13.45");
    friendMap.put("mfyf", "192.168.13.45");
    System.out.println("Friends List!");
    System.out.println("**********************************");
    System.out.println("User name\t\t\tip");
    for (Map.Entry<String, String> entry : friendMap.entrySet()) {
        System.out.println(entry.getKey() + "\t" + entry.getValue());
    }
    System.out.println("**********************************");
}

}

Then the client side sends the thread class ClientSendRunnable

In this class, I add an ArrayList state collection, which only stores one element to synchronize the state of the sending and receiving threads on the client side. In order to enable both threads to access this state, I define ArrayList (of course, in the Client class, defining an ordinary member variable can also use the static keyword. At that time, I used the static keyword to synchronize the state of the sending and receiving threads on the client side). Unexpectedly, the role of state: When the Send thread requests closure, it should modify the state first, and the receive thread should terminate itself when accessing the state.
package com.Test.com;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.ArrayList;

public class ClientSendRunnable implements Runnable {
private Socket socket;
private String receiveUserName;
private String receiveIp;
ArrayList state;
public ClientSendRunnable(Socket socket, String receiveUserName, String receiveIp,ArrayListstate) {
this.socket = socket;
this.receiveUserName = receiveUserName;
this.receiveIp = receiveIp;
this.state=state;
}

@Override
public void run() {
    try {
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(socket.getOutputStream());
        SerialiableMessage serialiableMessage=new SerialiableMessage();
        serialiableMessage.setSendUserNmae(socket.getLocalAddress().getHostName());
        serialiableMessage.setSendIp(socket.getLocalAddress().getHostName());
        serialiableMessage.setReceiveUserName(receiveUserName);
        serialiableMessage.setReceiveIp(receiveIp);

        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
        String inLine;
        System.out.println("Please enter chat information!");
        System.out.println("*****************");
        while ((inLine=bufferedReader.readLine())!=null){
            serialiableMessage.setMessage(inLine);
            objectOutputStream.writeObject(serialiableMessage);
            System.out.println("[to]"+serialiableMessage.getReceiveUserName());
            System.out.println("[message]:"+serialiableMessage.getMessage());
            objectOutputStream.reset();
            if (inLine.equals("exit")) {
                state.set(0,false);
                serialiableMessage.setReceiveUserName(serialiableMessage.getSendUserNmae());
                serialiableMessage.setReceiveIp(serialiableMessage.getSendIp());
                objectOutputStream.writeObject(serialiableMessage);
                objectOutputStream.reset();
                objectOutputStream.close();
                break;
            }
            System.out.println("Successful delivery!\n");
            System.out.println("*****************");
            System.out.println("Please enter chat information!");
        }
        Thread.sleep(1000);
        socket.shutdownOutput();
        socket.close();
    } catch (IOException e) {
    } catch (InterruptedException e) {
    }
}

}

Client Receive Runnable

Each message received by the client receiving thread determines whether it is a user list or not. If it is a user list, it updates the local user list or prints the message.

package com.Test.com;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;

public class ClientReceiveRunnable implements Runnable{
private Socket socket;
private HashMap<String,String>friendMap;
ArrayList threadState;
public ClientReceiveRunnable(Socket socket, HashMap<String, String> friendMap,ArrayListthreadState) {
this.socket = socket;
this.friendMap = friendMap;
this.threadState=threadState;
}

@Override
public void run() {
    try {
        System.out.println("Client begins to receive messages!");
        ObjectInputStream objectInputStream=new ObjectInputStream(socket.getInputStream());
        SerialiableMessage serialiableMessage;
        while ((serialiableMessage=(SerialiableMessage)objectInputStream.readObject())!=null){
            if(threadState.get(0).equals(false)){
                objectInputStream.close();
                break;
            }
            if (serialiableMessage.getMessage().equals("accountList")) {
                if (serialiableMessage.getReceiveUserName().equals("login")) {
                    friendMap.put(serialiableMessage.getSendUserNmae(),serialiableMessage.getSendIp());
                }else {
                    friendMap.remove(serialiableMessage.getSendUserNmae());
                }
            }else {
                System.out.println("Receive the message!");
                System.out.println("[from]  "+serialiableMessage.getSendUserNmae());
                System.out.println("[message]:  "+serialiableMessage.getMessage());
            }
        }
    } catch (IOException e) {
    } catch (ClassNotFoundException e) {
    }
}

}

The server class ServerMachine is defined below

Such methods include the main method, listening for visiting client requests, obtaining local online users of LAN, etc. When listening for new client requests to connect, new users are added to the online user list. The server sending thread updates the online user list to all clients at regular intervals.

package com.Test.com;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

public class ServerMachine{
public static void ServerMachine() throws IOException {
ArrayList allLocalIp = GainAllLocalUSER.ScannerAllLocalIP();
String serveMachineip = InetAddress.getLocalHost().getHostAddress();
LinkedList accountLists=new LinkedList();
ServerSocket serverMachine=new ServerSocket(8000);
System.out.println("Server starts listening on port 8000!");
while (true) {
ServeBordcast serveBordcast=new ServeBordcast(allLocalIp,serveMachineip);
Thread bordServeMachineIp=new Thread(serveBordcast);
bordServeMachineIp.start();
Socket accept = serverMachine.accept();
InetAddress userInetAddress = accept.getInetAddress();
String userName = userInetAddress.getHostName();
SerialiableMessage serialiableUser = new SerialiableMessage(userName, userInetAddress.getHostAddress(),"login", "accountList");
accountLists.add(serialiableUser);
ServerReceiveRunnable serverReceiveRunnable = new ServerReceiveRunnable(accept);
ServerSendRunnable serverSendRunnable = new ServerSendRunnable(accept,accountLists);
Thread receiveThread = new Thread(serverReceiveRunnable);
Thread sendThread = new Thread(serverSendRunnable);
sendThread.start();
receiveThread.start();
}
}

public static void main(String[] args) throws IOException {
    ServerMachine();
}

public static class GainAllLocalUSER {
    public static ArrayList<String> ScannerAllLocalIP() throws IOException {
        ArrayList<String> ipList=new ArrayList();
        Runtime runtime=Runtime.getRuntime();
        Process process=runtime.exec("arp -a");
        StringBuilder stringBuilder=new StringBuilder();
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(process.getInputStream(),"GBK"));
        String line;
        while ((line=bufferedReader.readLine())!=null){
            if (line.length()==0) {
                int count=0;
                while (count<3){
                    line=bufferedReader.readLine();
                    count++;
                }
            }
            String[] split = line.split(" ");
            ipList.add(split[2]);
        }
        return ipList;
    }

    public static ArrayList<String> ScannerAllLocalAccount(ArrayList<String> ipList) throws UnknownHostException {
        ArrayList<String> accountList=new ArrayList<>();
        for (int i = 0; i <ipList.size() ; i++) {
            InetAddress account = InetAddress.getByName(ipList.get(i));
            accountList.add(account.getHostName());
        }
        Iterator<String> iterator=accountList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        return accountList;
    }
    public static ArrayList<String> ScannerAllLocalAccount() throws IOException {
        ArrayList<String> ipList = GainAllLocalUSER.ScannerAllLocalIP();
        ArrayList<String> accountList=new ArrayList<>();
        for (int i = 0; i <ipList.size() ; i++) {
            InetAddress account = InetAddress.getByName(ipList.get(i));
            System.out.println(account.getHostName());
            accountList.add(account.getHostName());
        }
        Iterator<String>iterator=accountList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("Return to local user ip Composition Success!");
        return accountList;
    }
}

}

Next, define the server message queue MessageList

Considering that there will be multiple users in the LAN, the client is also divided into sending thread and receiving process.
So you need to define a common memory area for temporarily storing all user messages, which is a LinkList collection of serialized messages SerialiableMessage s

package com.Test.com;

import java.util.LinkedList;

public class MessageList {
static LinkedListmessagesLinkList=new LinkedList<>();
public static LinkedList gainMessageList(){
return messagesLinkList;
}
}

The server sending thread class ServerSendRunnable is defined below

This thread traverses through the MessageList collection in a dead loop, extracts the receiveIp value of the record from the message queue, and compares the receiveIp with the client ip connected by this thread. If matched, it indicates that the message is the message that the current thread should send, so the current thread sends the message and deletes the message from the message queue. This thread deletes the message every other time. At this time, the message content is changed to accountList, which identifies this as the user list.

package com.Test.com;

import com.Test.com.MessageList;
import com.Test.com.SerialiableMessage;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.LinkedList;

public class ServerSendRunnable implements Runnable {
private Socket accept;
private LinkedList accountLists;

public ServerSendRunnable(Socket accept, LinkedList<SerialiableMessage> accountLists) {
    this.accept = accept;
    this.accountLists = accountLists;
}

@Override
public void run() {
    try {
        System.out.println("*by"+accept.getInetAddress().getHostName()+"Create a sending thread!");
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(accept.getOutputStream());
        String ip = accept.getInetAddress().getHostAddress();
        long starttime = System.currentTimeMillis();
        while (true){
            long endtime = System.currentTimeMillis();
            if ((endtime-starttime)%60000<5000) {
                for (int i = 0; i <accountLists.size() ; i++) {
                    objectOutputStream.writeObject(accountLists.get(i));
                }
            }
            LinkedList<SerialiableMessage> messagesLinkList = MessageList.gainMessageList();
            for (int i = 0; i <messagesLinkList.size() ; i++) {
                SerialiableMessage serialiableMessage = messagesLinkList.get(i);
                if (serialiableMessage.getMessage().equals("exit")) {
                    for (int j = 0; j <accountLists.size() ; j++) {
                        if (accountLists.get(j).getSendUserNmae().equals(serialiableMessage.getSendUserNmae())) {
                            accountLists.remove(j);
                            objectOutputStream.close();
                            break;
                        }
                    }
                    messagesLinkList.remove(i);
                    break;
                }
                if (ip.equals(serialiableMessage.getReceiveIp())) {
                    objectOutputStream.writeObject(serialiableMessage);
                    messagesLinkList.remove(i);
                }
            }
        }
    } catch (IOException e) {
    }
}

}

Next, define ServerReceiveRunnable, the server acceptor thread class

This thread receives messages from the client and stores them in the message queue so that the sending thread can fetch and send them.

package com.Test.com;

import com.Test.com.MessageList;
import com.Test.com.SerialiableMessage;

import java.io.*;
import java.net.Socket;
import java.util.LinkedList;

public class ServerReceiveRunnable implements Runnable{
private Socket accept;

public ServerReceiveRunnable(Socket accept) {
    this.accept = accept;
}

@Override
public void run() {
    try {
        System.out.println("*by"+accept.getInetAddress().getHostName()+"Create a receiving thread!");
        ObjectInputStream objectInputStream=new ObjectInputStream(accept.getInputStream());
        SerialiableMessage messageLine;
        System.out.println("**********************************\n");

        /*****************************************/
        while ((messageLine =(SerialiableMessage)objectInputStream.readObject()) != null) {
            System.out.println("[from] "+messageLine.getSendUserNmae()+"  [to] "+messageLine.getReceiveUserName());
            System.out.println("[message]:  "+messageLine.getMessage());
            LinkedList<SerialiableMessage> messagesLinkList = MessageList.gainMessageList();
            messagesLinkList.add(messageLine);
            System.out.println("\n");
            System.out.println("**********************************\n");
        }
        objectInputStream.close();
    } catch (IOException e) {
    } catch (ClassNotFoundException e) {
    }finally {
        try {
            accept.close();
        } catch (IOException e) {
        }

    }
}

}

The following thread class, ServeBordcast, was added later.

Considering that the servers are different computers and the IP may be different after each reboot, this server thread class is added. This thread will use UDP co-broadcast server IP to the LAN online client at intervals. There is a multithreaded preemptive access local IP conflict, if so, stop starting this thread in Server Machine class, delete the section waiting for server response in Client class client() method, and modify "Socket socket = new Socket (server MachineIp, 8000);" in which server MachineIp can be changed to actual server ip)

package com.Test.com;

import java.io.IOException;
import java.net.*;
import java.util.ArrayList;

public class ServeBordcast implements Runnable {
private ArrayList allLocalIp;
private String serveMachineIp;

public ServeBordcast(ArrayList<String> allLocalIp, String serveMachineIp) {
    this.allLocalIp = allLocalIp;
    this.serveMachineIp = serveMachineIp;
}

@Override
public void run() {
    while (true) {
        for (int i = 0; i < allLocalIp.size(); i++) {
            DatagramSocket ds = null;
            try {
                ds = new DatagramSocket();
            } catch (SocketException e) {
            }
            DatagramPacket dp = null;
            try {
                dp = new DatagramPacket(serveMachineIp.getBytes(), serveMachineIp.getBytes().length, InetAddress.getByName(allLocalIp.get(i)), 5050);
            } catch (UnknownHostException e) {
            }
            try {
                ds.send(dp);
            } catch (IOException e) {
            }
            ds.close();
        }
        System.out.println("Broadcast Server ip Success!");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
    }
}

}
The first blog, there should be BUG, haha.

Topics: Java socket