📄 738-742.html
字号:
<HTML>
<HEAD>
<META name=vsisbn content="1571690433"><META name=vstitle content="Black Art of Java Game Programming"><META name=vsauthor content="Joel Fan"><META name=vsimprint content="Sams"><META name=vspublisher content="Macmillan Computer Publishing"><META name=vspubdate content="11/01/96"><META name=vscategory content="Web and Software Development: Programming, Scripting, and Markup Languages: Java"><TITLE>Black Art of Java Game Programming:The Internet MahJong Server</TITLE>
<!-- HEADER --><STYLE type="text/css"> <!-- A:hover { color : Red; } --></STYLE><META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW"><script><!--function displayWindow(url, width, height) { var Win = window.open(url,"displayWindow",'width=' + width +',height=' + height + ',resizable=1,scrollbars=yes'); if (Win) { Win.focus(); }}//--></script><SCRIPT><!--function popUp(url) { var Win = window.open(url,"displayWindow",'width=400,height=300,resizable=1,scrollbars=yes'); if (Win) { Win.focus(); }}//--></SCRIPT><script language="JavaScript1.2"><!--function checkForQuery(fm) { /* get the query value */ var i = escape(fm.query.value); if (i == "") { alert('Please enter a search word or phrase'); return false; } /* query is blank, dont run the .jsp file */ else return true; /* execute the .jsp file */}//--></script></HEAD><BODY>
<TABLE border=0 cellspacing=0 cellpadding=0>
<tr>
<td width=75 valign=top>
<img src="../1571690433.gif" width=60 height=73 alt="Black Art of Java Game Programming" border="1">
</td>
<td align="left">
<font face="arial, helvetica" size="-1" color="#336633"><b>Black Art of Java Game Programming</b></font>
<br>
<font face="arial, helvetica" size="-1"><i>by Joel Fan</i>
<br>
Sams, Macmillan Computer Publishing
<br>
<b>ISBN:</b> 1571690433<b> Pub Date:</b> 11/01/96</font>
</td>
</tr>
</table>
<P>
<!--ISBN=1571690433//-->
<!--TITLE=Black Art of Java Game Programming//-->
<!--AUTHOR=Joel Fan//-->
<!--AUTHOR=Eric Ries//-->
<!--AUTHOR=Calin Tenitchi//-->
<!--PUBLISHER=Macmillan Computer Publishing//-->
<!--IMPRINT=Sams//-->
<!--CHAPTER=18//-->
<!--PAGES=738-742//-->
<!--UNASSIGNED1//-->
<!--UNASSIGNED2//-->
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="735-738.html">Previous</A></TD>
<TD><A HREF="../ewtoc.html">Table of Contents</A></TD>
<TD><A HREF="742-746.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
<P><BR></P>
<P>One way to prevent such deadlocks is to make all threads acquire these monitors only in a certain order. In my case, the order is Player instances first, then Table.global, then Table instances, and finally Player.global. The particular ordering isn’t a big deal; what matters is consistency. If all monitors are acquired in order, then deadlock cannot happen. This is why the first version of Table.leave() is used.
</P>
<H3><A NAME="Heading22"></A><FONT COLOR="#000077">The Basic Client</FONT></H3>
<P>The client is a Java applet that’s accessible from a user’s Web browser. However, unlike most Java applets, the MahJong client needs to live in its own window (a Frame in Abstract Windowing Toolkit (AWT) terminology). This allows the user to continue using his browser while on the server, and also makes it easier to integrate menu bars. In fact, as I added more features, I found it necessary to open multiple windows.
</P>
<P>I therefore define two classes, first a Greeting class that extends Applet and lives inside a Web page, and then a Client class that extends Frame. The Greeting applet simply presents a clickable button. When the button is clicked on, it connects to the server, and creates a new Client instance, whose job is to carry on.</P>
<H4 ALIGN="LEFT"><A NAME="Heading23"></A><FONT COLOR="#000077">The Greeting Class and the HTML Document</FONT></H4>
<P>The Greeting class is straightforward to write:
</P>
<!-- CODE //-->
<PRE>
package client;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.applet.*;
public class Greeting extends Applet {
Button main_button = new Button("Connect NOW!");
Client main = null;
public void init () {
add(main_button);
show();
}
public void restart () {
main = null;
main_button.setLabel("Connect again");
main_button.enable();
}
public synchronized boolean action (Event evt, Object what) {
if (evt.target == main_button) {
main_button.disable();
try {
Socket s = new Socket(getParameter("ServerHost"), 5656);
DataInputStream i = new DataInputStream(s.getInputStream());
DataOutputStream o = new DataOutputStream(s.getOutputStream());
main_button.setLabel("Connected!");
main = new Client(this, s, i, o);
} catch (IOException e) {
restart();
}
}
return true;
}
}
</PRE>
<!-- END CODE //-->
<P>We also need to write an HTML document, say mj.html, and embed our applet in it. This is done via the <applet> tag:
</P>
<!-- CODE SNIP //-->
<PRE>
<html>
<applet CODE="client.Greeting.class" WIDTH=200 HEIGHT=40>
<param NAME="ServerHost" VALUE="localhost">
</applet>
</html>
</PRE>
<!-- END CODE SNIP //-->
<P>If you run a public server, the ServerHost parameter should contain the actual hostname of the machine on which you are running your server; otherwise, people won’t be able to connect to it. Of course, you also want to explain the game in the HTML document.
</P>
<P ALIGN="RIGHT"><B>classes; MahJong; CPlayer</B></P>
<H4 ALIGN="LEFT"><A NAME="Heading24"></A><FONT COLOR="#000077">The Client Class</FONT></H4>
<P>On the client side, we also have the notion of players and tables. I define classes CPlayer and CTable. They are miniature versions of their server-side counterparts. We need to define their toString() methods. For a player, it formats his ID, name, and table ID. For a table, it formats the IDs of the table and the players on that table. For reasons I’ll get to later, the toString() methods need to save a copy of their output to be retrieved later by a getString() method. I won’t go into more details here.
</P>
<P>The client needs a listener thread to receive and dispatch packets from the server. Therefore, we need to make Client implement Runnable. Its run() method is roughly sketched out in an earlier section on packet handling. The code shown below is quite similar to the Player class, except that here we do not need a replier thread.</P>
<!-- CODE //-->
<PRE>
package client;
import java.awt.*;
import java.io.*;
import java.net.*;
class Client extends Frame implements Runnable {
Greeting greeter;
Socket sock;
DataInputStream in;
DataOutputStream out;
Thread listener;
CPlayer[] g_players = new CPlayer[100];
CTable[] g_tables = new CTable[25];
int myID = -1;
Client (Greeting g, Socket s,
DataInputStream i, DataOutputStream o) {
sock = s;
in = i;
out = o;
greeter = g;
// spawn listener thread
(listener = new Thread(this)).start();
// other initializations here...
}
void output (byte[] p) {
if (p != null && p.length > 0) {
try {
out.write(p, 0, p.length);
} catch (IOException e) {
disconnect();
listener.stop();
dispose();
greeter.restart();
}
}
}
void disconnect () {
try {
in.close();
out.close();
sock.close();
} catch (IOException e) { }
}
public void run () {
loop:
while (true) {
// packet dispatcher code here, shown earlier
}
// handle bad connection
disconnect();
dispose();
greeter.restart();
}
void hdYouAre () throws IOException { // packet handler for SP_YOU_ARE
myID = (int)in.readByte();
}
// ...
}
</PRE>
<!-- END CODE //-->
<H4 ALIGN="LEFT"><A NAME="Heading25"></A><FONT COLOR="#000077">The Client GUI</FONT></H4>
<P>The first GUI element of the client is a login window. It should be presented as soon as we get the connection to the server. To simplify matters, I use here only a text input field to accept a login name. Patch up the constructor of Client and define an action() method:
</P>
<!-- CODE //-->
<PRE>
// in class Client
TextField login_name;
public Client ( ... ) {
// ...
login_name = new TextField(20);
setLayout(new FlowLayout());
add(login_name);
pack();
show();
}
public synchronized boolean action (Event evt, Object what) {
if (evt.target == login_name) {
login_name.setEditable(false);
// exercise: write Packet.CPLogin()...
output(Packet.CPLogin(login_name.getText()));
}
// other events
return true;
}
</PRE>
<!-- END CODE //-->
<P>The server is supposed to process our request and send back an SP_LOGIN packet. We handle it in the following way:
</P>
<!-- CODE //-->
<PRE>
void hdLogin () throws IOException {
byte result = in.readByte(); // read in packet data
if (result != Packet.LOGIN_OK) { // login rejected
login_name.setEditable(true);
login_name.setText("");
return;
} else { // login accepted
after_login();
}
}
</PRE>
<!-- END CODE //-->
<P><BR></P>
<CENTER>
<TABLE BORDER>
<TR>
<TD><A HREF="735-738.html">Previous</A></TD>
<TD><A HREF="../ewtoc.html">Table of Contents</A></TD>
<TD><A HREF="742-746.html">Next</A></TD>
</TR>
</TABLE>
</CENTER>
</BODY>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -