我正在学习套接字以及服务器/客户端如何通信。
到目前为止,我的协议已经找到并设法模拟合成TCP三次握手:
多数民众赞成,所有这些都是在新线程中我的Runnable客户端服务的while(true)循环下的Run()方法中线性执行的。我不知道GUI类中的Button如何告诉我的客户端服务发送特定数据包,例如,当服务在不同的线程中运行时,按下UI中的特定按钮。
我有一个想法,但在此纠正我:不知何故为我的GUI中的所有按钮添加ActionListeners而不是GUI ..
感谢。
PS。我正在使用DataInputStream和DataOutputStream从/向套接字流读取/写入数据。
ClientService.java:
public class ClientService implements Runnable, GameProtocol {
private Socket socket;
private int clientNumber;
private GameClient client;
private JTextArea clientConsole;
private DataInputStream fromServer;
private DataOutputStream toServer;
public ClientService(Socket aSocket, GameClient aClient, JTextArea textArea) {
this.socket = aSocket;
this.client = aClient;
this.clientConsole = textArea;
}
private void buildStreams() throws Exception {
this.fromServer = new DataInputStream(this.socket.getInputStream());
this.toServer = new DataOutputStream(this.socket.getOutputStream());
}
public void sendPacket(int data) {
try {
this.toServer.writeInt(data);
} catch (Exception e) {
e.printStackTrace();
}
return;
}
public void flushPacket() {
try {
this.toServer.flush();
} catch (Exception e) {
e.printStackTrace();
}
return;
}
public void run() {
try {
try {
buildStreams();
toServer.writeInt(PLAYER_SYN); // start synthetic three way handshake
toServer.flush();
if(fromServer.readInt() == SERVER_SYNACK) {
this.clientNumber = fromServer.readInt();
clientConsole.append("Client number from Server: " + clientNumber + "\n");
toServer.writeInt(PLAYER_ACK);
toServer.writeInt(this.clientNumber);
toServer.flush();
}
else {
clientConsole.append("Client -> Server Sync failed. Can't proceed.\n");
toServer.writeInt(PLAYER_QUIT);
toServer.flush();
socket.close();
System.exit(-1);
}
executeCommand();
} finally {
socket.close();
}
} catch (Exception e) {
System.out.println("Client Service: " + e.getMessage());
}
}
private void executeCommand() throws Exception {
boolean quit = false;
while(!quit) {
int command = -14324;
if(fromServer.available() > 0) {
command = fromServer.readInt();
}
switch (command) {
case WINNER:
clientConsole.append("You Win.\n");
break;
case LOSER:
clientConsole.append("You Lost.\n");
break;
case ENABLE_TURN:
client.enableTurn();
break;
case DISABLE_TURN:
client.disableTurn();
break;
}
}
}
}
GameClient.java:
public class GameClient extends JFrame implements GameProtocol {
/**
* TextArea size.
*/
private final int
TEXTAREA_ROWS = 5,
TEXTAREA_COLS = 40;
/**
* Client window size, unadjustable.
*/
private final int
FRAME_W = 535,
FRAME_H = 600;
// for Assignment 10
private Socket socket;
private DataOutputStream toServer;
private DataInputStream fromServer;
/**
* ArrayList of type Cards, by default, holds 20 cards.
*/
private ArrayList<Card> cards;
/**
* Console for debugging.
*/
private JTextArea gameConsole;
/**
* Button to terminate game
*/
private JButton quitButton;
/**
* Main Frame panels, cardsPanel and consolePanel are sub-panels of mainPanel.
*/
private JPanel mainPanel, cardsPanel, consolePanel;
/**
* Builds packets to be sent to the server
*/
private Packet packet;
/**
* players points, accumulated.
*/
private int points = 0;
/**
* card choice 1
*/
private int choice1 = -1;
/**
* card choice 2
*/
private int choice2 = -1;
/**
* for building packet arguments
*/
private static int packetBuilderCount = 0;
/**
* Counter for outbound and inbound packets
*/
private static int packetNumber = 1;
/**
* for timing out cards when recieving no match
*/
private Timer timer;
/**
* true when user is allowed to pick cards.
* false when another user is true
*/
private boolean isTurn = true;
// service
private ClientService service;
/**
* default contrustor. Builds JFrame and all components.
*/
public static void main(String[] args) {
new GameClient();
}
public GameClient() {
packet = new Packet();
buildButton();
buildCards();
addEventToCards(); // adds actionlistener to each card.
buildPanel();
buildTimer();
buildFrame();
buildConnection(); // for assignment 10
}
private void buildConnection() {
try {
this.socket = new Socket(HOST, PORT);
// this.toServer = new DataOutputStream(socket.getOutputStream());
// this.fromServer = new DataInputStream(socket.getInputStream());
gameConsole.append("Socket Connected\n");
service = new ClientService(socket, this, this.gameConsole);
new Thread(service).start();
} catch (SecurityException ex) {
System.out.println("Check your firewall or antivirus. Unable to establish connection to server.");
} catch (UnknownHostException ex) {
System.out.println("Host can't be found. Unable to establish connection to server.");
} catch (ConnectException ex) {
System.out.println("Connection refused. What now?");
ex.printStackTrace();
// ex.printStackTrace();
} catch (IOException ex) {
System.out.println("fuck......");
}
}
/**
* initializes the timer.
*/
private void buildTimer() {
int delay = 3000; // wait for 3000ms
timer = new Timer(delay, new AbstractAction() {
/**
* event handler for timer. Flips the cards back to face down position.
*/
@Override
public void actionPerformed(ActionEvent ae) {
flipCard(choice1, 10);
flipCard(choice2, 10);
}
});
}
/**
* Initializes JFrame
*/
private void buildFrame() {
this.setSize(FRAME_W, FRAME_H);
setResizable(false);
this.add(mainPanel);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
/**
* Initializes and compiles panels together. Adds Cards to panels
*/
private void buildPanel() {
gameConsole = new JTextArea(TEXTAREA_ROWS, TEXTAREA_COLS);
JScrollPane scrollConsole = new JScrollPane(gameConsole);
scrollConsole.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
gameConsole.setEditable(false);
mainPanel = new JPanel();
mainPanel.setPreferredSize(new Dimension(535, 475));
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
cardsPanel = new JPanel(new GridLayout(4, 5, 20, 20));
for(int i = 0; i < 20; i++) {
cardsPanel.add(this.cards.get(i));
}
consolePanel = new JPanel();
consolePanel.add(scrollConsole);
consolePanel.add(quitButton);
mainPanel.add(cardsPanel);
mainPanel.add(consolePanel);
}
/**
* builds quit button, adds action listener to handle click
*/
private void buildButton() {
quitButton = new JButton("Quit");
quitButton.addActionListener(e -> {
buildQuitPacket();
});
}
/**
* builds 20 Card object.
*/
private void buildCards() {
cards = new ArrayList<Card>();
Card temp;
for(int i = 0; i < 20; i++) {
temp = new Card();
cards.add(temp);
}
}
/**
* adds actionlistener to all 20 card objects.
*/
private void addEventToCards() {
for(int i = 0; i < 20; i++) {
final int INDEX = i;
cards.get(i).addActionListener(e -> buildButtonPacket(this.cards.get(INDEX).getId()));
}
}
/**
* Builds and sends a Packet when a card is chosen
* @param value The card ID which was chosen.
*/
private void buildButtonPacket(int value) {
if(!isTurn) {
this.gameConsole.append("Its not you're turn.\n");
return;
}
if(packetBuilderCount == 0) {
packet.writeCommandToPacket(PICKED_CARDS);
packet.writeValueToPacket(value);
packetBuilderCount++;
choice1 = value;
}
else if(packetBuilderCount == 1) {
packet.writeValueToPacket(value);
packetBuilderCount = 0;
writeToConsoleOutBound(packet.toString());
choice2 = value;
// try{
// out.print(this.packet.getPacket());
// out.flush();
// } catch (Exception e) {
// gameConsole.append("could not send packet.\n");
// }
packet.clearPacket();
}
}
/**
* For handling commands from recieved Packet
* @param recieved the Packet object recieved. Gets and handles command and possible arguments that follows.
* See GameProtocol class for packet command definitions.
*/
public void handlePacket(Packet recieved) {
int cmd = recieved.getCommand();
writeToConsoleInBound(recieved.toString());
int arg1 = -99;
int arg2 = -99;
switch (cmd) {
case SERVER_SYNACK:
arg1 = recieved.getFirstArg();
if(arg1 == 1) {
this.isTurn = true;
}
else if (arg1 == 0) {
this.isTurn = false;
}
else {
writeToConsoleError("Server sync failed. Terminating..");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
writeToConsoleError(ex.getMessage());
} finally {
System.exit(-1);
}
}
break;
case CARDMATCH:
arg1 = recieved.getFirstArg();
arg2 = recieved.getSecondArg();
if(arg1 > 9) {
arg1 = 9;
}
if(arg2 > 9) {
arg2 = 9;
}
flipCard(choice1, arg1);
flipCard(choice2, arg2);
break;
case CARDNOMATCH:
// System.out.println("CLIENT: im going to sleep");
arg1 = recieved.getFirstArg();
arg2 = recieved.getSecondArg();
if(arg1 > 9) {
arg1 = 9;
}
if(arg2 > 9) {
arg2 = 9;
}
flipCard(choice1, arg1);
flipCard(choice2, arg2);
this.cardsPanel.revalidate();
this.cardsPanel.repaint();
this.consolePanel.revalidate();
this.consolePanel.repaint();
timer.setRepeats(false); //the timer should only go off once
timer.start();
break;
case SHOW_CARD:
arg1 = recieved.getFirstArg();
arg2 = recieved.getSecondArg();
if(arg1 < 0 || arg1 > 19) {
writeToConsoleError(" BAD PACKET ARGUMENT 1, \nCard index out of bounds");
}
else if(arg2 < 0 || arg2 > 10) {
writeToConsoleError(" BAD PACKET ARGUMENT 2, \nImage index out of bounds");
}
else {
flipCard(arg1, arg2);
}
break;
case ENABLE_TURN:
this.isTurn = true;
break;
case DISABLE_TURN:
this.isTurn = false;
break;
case WINNER:
this.gameConsole.append("You win!\n");
break;
case LOSER:
this.gameConsole.append("You lost :( \n");
break;
case ADD_POINTS:
arg1 = recieved.getFirstArg();
this.points += arg1;
this.gameConsole.append("** Gained " + Integer.toString(arg1) + ", you now have " + Integer.toString(this.points) + " points.\n");
break;
case OTHER_QUIT:
this.gameConsole.append("Other player forfeited. You win!\n");
break;
}
}
/**
* Builds a packet containing QUIT when quit button is pressed
* Only available when isTurn == true.
*/
private void buildQuitPacket() {
if(this.isTurn == false) {
this.gameConsole.append("You can only quit when it is your turn.");
return;
}
service.sendPacket(PLAYER_QUIT);
service.flushPacket();
// this.service.sendPacket(PLAYER_QUIT);
// this.service.flushPacket();
System.exit(0);
}
/**
* Writes the outgoing packet to the game console.
* @param msg the actual packet message.
*/
public void writeToConsoleOutBound(String msg) {
this.gameConsole.append(Integer.toString(packetNumber++) + ": SENDING: " + msg + "\n");
}
/**
* Writes the incoming packet to the game console.
* @param msg the actual packet message.
*/
public void writeToConsoleInBound(String msg) {
this.gameConsole.append(Integer.toString(packetNumber++) + ": RECEIVING: " + msg + "\n");
}
/**
* Writes an error to the game console.
* @param msg the error message.
*/
public void writeToConsoleError(String msg) {
this.gameConsole.append("Error handling packet number " + Integer.toString(packetNumber) + "." + msg + "\n");
}
/**
* Flips a Card object and show an image
* @param whichCard the card ID which will be flipped.
* @param imgID the image which will be shown on the flipped card.
*/
public void flipCard(int whichCard, int imgID) {
this.cards.get(whichCard).flipCard(imgID);
}
/**
* Accumulates points.
* @param amt the amount to be added.
*/
public void gainPoints(int amt) {
this.points += amt;
}
public void enableTurn() {
this.isTurn = true;
}
public void disableTurn() {
this.isTurn = false;
}
}
答案 0 :(得分:0)
您与套接字的连接有效,因为它位于构造函数中,没有调用已修改的元素(比如更新按钮的文本)。您的客户端服务需要一个循环来等待UI发出的请求,否则您需要创建一个包含UI发出的每个请求的线程。它将成为一个瓶颈,特别是在一个用户操作可以触发多个UI更新的游戏中。看看发布的代码你应该或多或少地遵循这种模式
package so;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
class ClientService implements Runnable{
JLabel label;
ConcurrentLinkedQueue<Long> work;
@Override
public void run() {
Long i = null;
try{
while(true){
if ((i = work.poll()) != null){
final long ii = i;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
label.setText(ii + "");
}
});
}
Thread.sleep(10);
}
}
catch(Exception x){
}
}
}
class GameClient extends JFrame{
Thread worker;
ConcurrentLinkedQueue<Long> work;
private JLabel label;
private JButton button;
GameClient(){
setLayout(new GridLayout(2, 1));
label = new JLabel("Label");
button = new JButton("Button");
button.addMouseListener(new MouseListener(){
@Override
public void mouseClicked(MouseEvent e) {
work.add(System.currentTimeMillis());
}
@Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
});
this.add(label);
this.add(button);
work = new ConcurrentLinkedQueue<Long>();
ClientService cs = new ClientService();
cs.label = this.label;
cs.work = this.work;
this.worker = new Thread(cs);
this.worker.start();
}
}
class SOCLass {
public static void main(String[] args){
SOCLass m = new SOCLass();
GameClient frame = new GameClient();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
frame.worker.interrupt();
System.exit(0);
}
});
frame.pack();
frame.setVisible(true);
}
}