Answer the question
In order to leave comments, you need to log in
How to correctly implement a client-server structure in this case?
The situation is this, there is a client-server application, a regular network chat. Sources: 3 modules Client, Server and the module responsible for the connection, the first and second modules depend on the third one. (Further shit code, and then the essence of the question):
Let's start with the third module, namely the network:
Interface TCPConnectionListener
public interface TCPConnectionListener {
void onConnectionReady(TCPConnection tcpConnection);
void onReceiveString(TCPConnection tcpConnection, String value);
void onDisconnect(TCPConnection tcpConnection);
void onExeption(TCPConnection tcpConnection, Exception e);
}
import java.io.*;
import java.net.Socket;
import java.nio.charset.Charset;
public class TCPConnection {
private final Socket socket;
private final Thread rxThread;
private final TCPConnectionListener eventListener;
private final BufferedReader in;
private final BufferedWriter out;
public TCPConnection(TCPConnectionListener eventListener, String ip, int port) throws IOException {
this(eventListener, new Socket(ip,port));
}
public TCPConnection(TCPConnectionListener eventListener, Socket socket)throws IOException {
this.socket =socket;
this.eventListener=eventListener;
in =new BufferedReader(new InputStreamReader(socket.getInputStream(), Charset.forName("UTF-8")));
out =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), Charset.forName("UTF-8")));
rxThread=new Thread(new Runnable() {
@Override
public void run() {
try {
eventListener.onConnectionReady(TCPConnection.this);
while (!rxThread.isInterrupted()){
eventListener.onReceiveString(TCPConnection.this, in.readLine());
}
} catch (IOException e) {
eventListener.onExeption(TCPConnection.this,e);
}finally {
eventListener.onDisconnect(TCPConnection.this);
}
}
});
rxThread.start();
}
public synchronized void sendMessage(String value){
try {
out.write(value+"\r\n");
out.flush();
} catch (IOException e) {
eventListener.onExeption(TCPConnection.this, e);
setDissconnect();
}
}
public synchronized void setDissconnect(){
rxThread.interrupt();
try {
socket.close();
} catch (IOException e) {
eventListener.onExeption(TCPConnection.this, e);
}
}
@Override
public String toString() {
return "TCPConnection: "+socket.getInetAddress()+" port: "+socket.getPort();
}
}
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
public class ChatServer implements TCPConnectionListener{
public static void main(String[]args){
new ChatServer();
}
private final ArrayList<TCPConnection> connections =new ArrayList<>();
private ChatServer(){
System.out.println("Server running...");
try(ServerSocket serverSocket=new ServerSocket(8189)){
while (true){
try {
new TCPConnection(this, serverSocket.accept());
}catch (IOException e){
System.out.println("TCPConnection exeption...");
}}
}catch (IOException e){
throw new RuntimeException(e);
}
}
@Override
public synchronized void onConnectionReady(TCPConnection tcpConnection) {
connections.add(tcpConnection);
sendToAllConnections("Client connect: "+tcpConnection);
}
@Override
public synchronized void onReceiveString(TCPConnection tcpConnection, String value) {
System.out.println("ttt");
sendToAllConnections(value);
}
@Override
public synchronized void onDisconnect(TCPConnection tcpConnection) {
connections.remove(tcpConnection);
sendToAllConnections("Client disconnect: "+tcpConnection);
}
@Override
public synchronized void onExeption(TCPConnection tcpConnection, Exception e) {
System.out.println("TCPConnections exeption...");
}
private void sendToAllConnections(String message){
System.out.println(message);
final int lh=connections.size();
for (int i=0;i<lh;i++){
connections.get(i).sendMessage(message);
}
}
}
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ClientWindow extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader =new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Hello World");
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 300, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import java.io.IOException;
public class Controller implements TCPConnectionListener{
@FXML
public TextArea dialogWn;
@FXML
public TextField nickname;
TCPConnection connection;
public void onToSendClick(MouseEvent mouseEvent) {
connection.sendMessage(nickname.getText());
}
public void onExitClicked(MouseEvent mouseEvent) {
try {
connection =new TCPConnection(this, "127.0.0.1", 8189);
} catch (IOException e) {
e.printStackTrace();
}
}
public void printMessage(String msg){
dialogWn.appendText(msg);
}
@Override
public void onConnectionReady(TCPConnection tcpConnection) {
}
@Override
public void onReceiveString(TCPConnection tcpConnection, String value) {
printMessage(value+"\n\r");
}
@Override
public void onDisconnect(TCPConnection tcpConnection) {
}
@Override
public void onExeption(TCPConnection tcpConnection, Exception e) {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="500.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<children>
<Label prefHeight="40.0" prefWidth="80.0" text="Ваш ник:" textFill="#3c39a3" underline="true" AnchorPane.leftAnchor="20.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="17.0" />
</font>
</Label>
<TextField fx:id="nickname" prefHeight="25.0" prefWidth="174.0" AnchorPane.rightAnchor="20.0" AnchorPane.topAnchor="8.0" />
<ScrollPane prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="40.0">
<content>
<TextArea fx:id="dialogWn" editable="false" prefHeight="408.0" prefWidth="298.0" />
</content>
</ScrollPane>
<Button fx:id="send" mnemonicParsing="false" onMouseClicked="#onToSendClick" prefHeight="25.0" prefWidth="100.0" text="Отправить" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="40.0" />
<Button fx:id="exit" mnemonicParsing="false" onMouseClicked="#onExitClicked" prefHeight="25.0" prefWidth="100.0" text="Выход" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="40.0" />
</children>
</AnchorPane>
Answer the question
In order to leave comments, you need to log in
In my opinion, it’s more or less written, not quite shitty code, you can improve
if there are 2 controllers - and you have different Windows / Forms, then none of them need to be inherited from TCPConnection and TCPConnectionListener, you need to make a Singleton somewhere
public static MyConnectionToServer
{
private static readonly TCPConnection Connection
= new .. (OnMessageReceived);
private static readonly Dictionary<string, List<Action<MessageBase>>> Listeners = new ..;
public static void SendToServer<T>(RequestBase request) { /* send */ }
private static void OnMessageReceived(string message)
{
// все сообщения наследовать например от MessageBase
// но в подклассах еще новые данные
// в MessageBase только string MessageType
// это десериализует только тип по сути, остальное игнорит
MessageBase messageBase = Deserialize<MessageBase>(message);
MessageBase concreteMessage = DeserializeConcrete(messageBase.MessageType, message);
Dispatch(messageBase.MessageType, concreteMessage);
}
private static T DeserializeConcrete<T>(string messageType, string message)
{
switch (messageType)
{
case "type1": return Deserialize<MessageType1>(message);
case "type2": return Deserialize<MessageType2>(message);
}
}
private static void Dispatch(string messageType, T concreteMessage) where T: MessageBase
{
// юзать Listeners по messageType и в них передать concreteMessage
}
public static void AddListener<T>(string messageType, Action<T> onReceived)
{
// добавить в Dictionary по messageType
}
public static void RemoveListener<T>(string messageType, Action<T> onReceived)
{
// удалить из Dictionary по messageType
}
}
а из каждого Контроллера Окна вызывать
AddListener("type1", (message) => ProcessType1(message));
и в конце RemoveListener("type1") чтобы не утекло
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question