- 根据网络通信完成最基本的消息发送,原理还是基于C/S,好的小二现在开始讲给你!
- 首先我们还是要建立服务端和客户端,并且消息的发送是基于客户端连接上服务端,消息的发送是利用IO流来进行发送的,并且不同类型的IO流可以发送不同类型的数据,这个就得根据实际情况情况来进行定义,这次的模仿qq可以将发送的消息看作字符串,因此最基本的额io流就可以,在发送的时候将其转化为字节数组就行。
- 下面是具体的步骤:
-
- 建立服务器端、客户端
-
- 服务器有自己的界面上面主要有发送消息框和发送按钮,还有显示全部消息的文本框,服务端一样
-
- 第一步完成客户端的消息发送,这个时候会用到什么呢?
按钮监听器,首先你在界面上添加按钮,当你的按钮按下则需要将文本框里的消息发送给所连接的服务端,这个需要怎么实现呢,之前都是新建一个类去实现ActionListener这个接口,现在可以直接使用匿名内部类代码如下:
- 第一步完成客户端的消息发送,这个时候会用到什么呢?
send.addActionListener(new ActionListener() {
//匿名内部类 可以不用传递参数了
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
String msg = tf.getText();
ta.append(dout.toString() + msg + "\r\n");
//ta.setText(msg);
//需要进行发送数据 先发送报文头
//发送字符串的长度
msg = dout.toString() + msg;
byte[] msgbyte = msg.getBytes();
int length = msgbyte.length;
try {
dout.writeByte(3);;
dout.writeInt(length);
dout.write(msgbyte);
dout.flush();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
tf.setText("");
}
});
明白了吗? 小二,当你按下按钮总共有3个小步骤:1.获取文本框里面你写的内容;2.将其通过io流发送给服务端;3.将文本框清空方便下一次的输入;
同理,服务短的消息发送是相同的
-
- 接下来,你的客户端发送消息了,那么你的服务端就必须要去读取消息,此时注意协议,以免消息读取错误。那么读取消息要怎么实现呢,试想,你建立的服务端当然是可以与很多客户端进行通信的而不是只能连接一个客户端,因此你就可以想到当然要添加一个循环了,让服务端循环去等待客户端的额连接,代码如下:
while(true) {
Socket client = server.accept();
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
DataInputStream din = new DataInputStream(in);
DataOutputStream dout = new DataOutputStream(out);
ServerThread st = new ServerThread(din,dout, ta,tf);
//send.addActionListener(st);
st.start();
}
当有客户端连接进来之后,首先是获取io流,然后去开启一个线程去完成对应的客户端的操作,注意这里有一句代码
al.add(st);
//这句有什么用,等下见分晓?
上面说到,有客户端接入进来就去开启一个线程,那么小二你肯定非常好奇开启的线程去做一些什么事情呢?我觉得要想让你明白首先你得看构造方法,看看传进来了什么参数
public ServerThread(DataInputStream din,DataOutputStream dout,TextArea ta,TextField tf) {
// TODO Auto-generated constructor stub
this.din = din;//数据输入流
this.ta = ta;//服务端界面的文本区域 就是显示所有消息的那个
this.tf = tf;//文本框 需要发送的消息就写在这里面
this.dout = dout;//数据输出流
}
小二看到这里你有没有一些想法呢?
开启线程的目的当然是为了将客户端发送的消息读取出来,因此run()方法里面一定是一个循环去读取消息,并且要按照指定的协议去读消息,代码:
public void run() {
// TODO Auto-generated method stub
try {
while(true) {
byte type = din.readByte();
if(type == 3) {
System.out.println("已经读到报文头 是要发送字符串");
int length = din.readInt();
byte[] msgbyte = new byte[length];
din.read(msgbyte);
String msg = new String(msgbyte);
ta.append(msg + "\r\n");
//ta.setText(msg + "\r\n");
}
}
}catch(Exception e) {
e.printStackTrace();
}
因此可想而知,客户端当去和服务端进行连接的时候,客户端也开启了一个线程去读取服务端的消息,代码:
public void run() {
try {
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
DataInputStream din = new DataInputStream(in);
DataOutputStream dout = new DataOutputStream(out);
while(true) {
byte type = din.readByte();
if(type == 3) {
System.out.println("已经读到报文头 是要发送字符串");
int length = din.readInt();
byte[] msgbyte = new byte[length];
din.read(msgbyte);
String msg = new String(msgbyte);
ta.append(msg + "\r\n");
//ta.setText(msg);
}
}
}catch(Exception e) {
e.printStackTrace();
}
}
-
- 说完了客户端首发消息,服务端收消息,还剩下什么,就是服务端发送消息,还有刚才的分晓!!!
小二,你可以将服务端看作一个总的基站,只要是连接这个服务端的客户端都可以收到服务端发送的消息,因此当服务端发送消息的时候,总的来看也是通过按钮监听器来完成的,但是想发给每一个与其连接的客户端这就要加一些东西了,我们就可以想象,当服务端发现一旦有客户端连接自己,就将这个客户端对象加入到一个队列里面去,就是与其对应的线程对象加入到队列里面去,那么当服务端发送消息的时候,就是遍历这个队列逐一进行消息的发送,注意队列里的对象都是线程对象因此想用这个对象进行消息的发送,则需要在这个线程类里面写一个发送消息的方法,并且要用到服务端界面的发送消息文本框,废话不多说,上代码:
- 说完了客户端首发消息,服务端收消息,还剩下什么,就是服务端发送消息,还有刚才的分晓!!!
public void sendString(String msg) {
//String msg = tf.getText();
ta.append("服务器:" + msg + "\r\n");
//ta.setText(msg);
//需要进行发送数据 先发送报文头
//发送字符串的长度
msg = "服务器:" + msg;
byte[] msgbyte = msg.getBytes();
int length = msgbyte.length;
try {
dout.writeByte(3);;
dout.writeInt(length);
dout.write(msgbyte);
dout.flush();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
tf.setText("");
}
小二,为师有些累,给为师接杯水,放点枸杞!!!