在后续的结合代码的讲解中,我会把红字的地方一一用黄色笔标出来,但是和代码中的命名可能不太一致,但是公式一模一样。
其次,不论是客户端socket还是服务端socket,都是可以又有输出流又有输入流的,也就是我既可以用客户端socket给人发消息,也可以用服务端socket给人发消息。
但是作为可以交互的软件,他是必须严格对称的。所以在这个程序中,小H和小Y两台电脑上,都是软件客户端负责发消息,也就是outputStream。也都是软件的服务器端负责接消息并分析类型,也就是inputStream。
好了,下面进入正题
MainFrame呢就是自己的主要窗口视图的意思。
好了字段介绍完毕,来看main函数吧
别紧张,整个run里面其他都是前端操作,只有一个frame.startSever和你有关系。这个函数先把用来侦听客户端儿子们的,自己的爸爸服务器端口初始化一下冷静一下 对就是那个chatSocketServer。来,背一下建立服务器侦听socket格式:
ServerSocket serversocket = new ServerSocket(8080);
然后我chatSocketServer身为服务器,我怎么开始监听呢,来再背一遍
Socket socket = ServerSocket.accept();
socket就是服务器爸爸,为了这个新的链接进来的客户端儿子创的一个名字。
反观上图startServer()的操作有点奇怪是不是,没看到这句accept(),反而是开了一个,ReceiveThread().start 表示开个线程,具体线程操作什么我们往下看。
|
|
服务器接受到消息之后其实很累的,因为他要判断各种各样的类型。
我们先背一遍服务器爸爸要怎么拿到客户端儿子的输入字节流
InputStream is= socket.getInputStream();
接下来再关注,这个字节流是怎么处理的
public class ReceiveThread extends Thread {
private final ServerSocket chatSocketServer;
MainFrame frame;
private String host;
/**
* 线程的主体方法
*
* @see java.lang.Thread#run()
*/
@Override
public void run() {
while (true) {
try {
frame.serverSocket = chatSocketServer.accept(); //!!!!前方高能
// chatSocketServer服务器,接收到的的别人的连接,赋值给frame.serverSocket
Socket serverSocket = frame.serverSocket;
host = serverSocket.getInetAddress().getHostName(); // 获取对方主机信息
String ip = serverSocket.getInetAddress().getHostAddress(); // 获取对方IP地址
int link = JOptionPane.showConfirmDialog(frame, "收到" + host
+ "的联机请求,是否接受?"); // 询问是否接受联机
if (link == JOptionPane.YES_OPTION) { // 如果接受联机
LoginPanel loginPanel = (LoginPanel) frame.getRootPane()
.getGlassPane(); // 获取登录面板的实例
loginPanel.setLinkIp(ip); // 设置登录面板的对家IP信息
}
serverSocket.setOOBInline(true); // 启用紧急数据的接收
InputStream is = serverSocket.getInputStream(); // 获取网络输入流
ObjectInputStream objis = new ObjectInputStream(is);// 创建对象输入流
while (frame.isVisible()) {
serverSocket.sendUrgentData(255); // 发送紧急数据
Object messageObj = objis.readObject(); // 从对象输入流读取Java对象
if (messageObj instanceof String) { // 如果读取的对象是String类型
String name = frame.getTowardsUser().getName();// 获取对家昵称
frame.appendMessage(name + ":" + messageObj); // 将字符串信息添加到通讯面板
} else if (messageObj instanceof byte[][]) { // 如果读取的是字节数组对象
GobangModel.getInstance().setChessmanArray( // 将数组对象设置为棋盘模型数据
(byte[][]) messageObj);
frame.getChessPanel1().getGobangPanel1().setTurn(true);// 获得走棋权限
byte myColor = frame.getChessPanel1().getGobangPanel1()
.getMyColor(); // 获取自己的棋子颜色
frame.getChessPanel1().getGobangPanel1().zhengliBoard(
myColor); // 整理棋盘
frame.getChessPanel1().backButton.setEnabled(true);// 悔棋按钮可用
} else if (messageObj instanceof Integer) {// 如果是整形对象
oprationHandler(messageObj);// 命令代码的接收和处理方法
} else if (messageObj instanceof UserBean) {// 如果是用户实体对象
UserBean user = (UserBean) messageObj;
frame.setTowardsUser(user); // 设置对家信息
}
}
} catch (SocketException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE,
null, ex);
JOptionPane.showMessageDialog(frame, "连接中断");
frame.getChessPanel1().reInit();
DefaultTableModel model = (DefaultTableModel) frame.userInfoTable.getModel();
model.setRowCount(0);
frame.getGlassPane().setVisible(true);
} catch (IOException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE,
null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE,
null, ex);
}
}
}
写到这里,软件充当服务端的时候是怎么做的,就已经全部写完了。如果这几个else if 这几个api调用是为了什么,你想弄得更明白一点,可能要去看项目了。
------------------------------------服务器端结束,客户端开始-------------------------------------
来,背一下客户端链接的socket的初始化格式,
Socket socket = new Socket(other’s_ip, other’s_port)
这个类要做的,就是把setsocket这个方法被loginButtonActionPerformed给调用一下,从而解决socket,然后这个socket要outputStream嘛要是没有的话它怎么给别人发消息是不是。
来,背一下
OutputStream os = sokect.getOutputStream();
把自己的socket设置清楚了看见没,而且自己的outputstream,到了别人那里会变成别人的inputStream哦请注意
Mainframe客户端写的是很好,但是他又不跟服务器端似得,直接在main函数就被触发了,那么客户端要怎么样才能被触发呢,怎么样才能去链接服务器爸爸呢,当然就是:
填写自己昵称,填写你要链接的对家IP地址
然后点击登录button的时候啦 你可以认为 他是客户端角色开始的入口