JAVAC/S QQ

这篇博客讲解了如何使用Java的Socket实现类似于QQ的消息发送功能。通过建立C/S架构,利用IO流进行数据传输,详细介绍了客户端和服务器端的消息发送与接收过程,包括按钮监听器的使用、线程的创建以及消息协议的重要性。服务端需要维持一个队列来管理与各个客户端的连接,以便广播消息。
摘要由CSDN通过智能技术生成
  • 根据网络通信完成最基本的消息发送,原理还是基于C/S,好的小二现在开始讲给你!
  • 首先我们还是要建立服务端和客户端,并且消息的发送是基于客户端连接上服务端,消息的发送是利用IO流来进行发送的,并且不同类型的IO流可以发送不同类型的数据,这个就得根据实际情况情况来进行定义,这次的模仿qq可以将发送的消息看作字符串,因此最基本的额io流就可以,在发送的时候将其转化为字节数组就行。
  • 下面是具体的步骤:
    1. 建立服务器端、客户端
    1. 服务器有自己的界面上面主要有发送消息框和发送按钮,还有显示全部消息的文本框,服务端一样
    1. 第一步完成客户端的消息发送,这个时候会用到什么呢?
      按钮监听器,首先你在界面上添加按钮,当你的按钮按下则需要将文本框里的消息发送给所连接的服务端,这个需要怎么实现呢,之前都是新建一个类去实现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.将文本框清空方便下一次的输入;
同理,服务短的消息发送是相同的

    1. 接下来,你的客户端发送消息了,那么你的服务端就必须要去读取消息,此时注意协议,以免消息读取错误。那么读取消息要怎么实现呢,试想,你建立的服务端当然是可以与很多客户端进行通信的而不是只能连接一个客户端,因此你就可以想到当然要添加一个循环了,让服务端循环去等待客户端的额连接,代码如下:
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();
		}
	}
    1. 说完了客户端首发消息,服务端收消息,还剩下什么,就是服务端发送消息,还有刚才的分晓!!!
      小二,你可以将服务端看作一个总的基站,只要是连接这个服务端的客户端都可以收到服务端发送的消息,因此当服务端发送消息的时候,总的来看也是通过按钮监听器来完成的,但是想发给每一个与其连接的客户端这就要加一些东西了,我们就可以想象,当服务端发现一旦有客户端连接自己,就将这个客户端对象加入到一个队列里面去,就是与其对应的线程对象加入到队列里面去,那么当服务端发送消息的时候,就是遍历这个队列逐一进行消息的发送,注意队列里的对象都是线程对象因此想用这个对象进行消息的发送,则需要在这个线程类里面写一个发送消息的方法,并且要用到服务端界面的发送消息文本框,废话不多说,上代码:
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("");
	}

小二,为师有些累,给为师接杯水,放点枸杞!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值