网络通信——1对1,多次通信

在上一篇文章《网络通信——1对1,单次通信》中实现了1对1,单次通信,本篇文章会实现1对1,多次通信。
任何需要通信的双方,都需要三个元素,Socket,DataInputStream和DataOutputStream,就能完成通信。

对于服务器端部分,当接收到来自客户端的“byebye”消息时,停止通信
对于客户端部分,需要实现用户从键盘输入,将输入的消息发送到服务器端,使用专门的方法接收键盘输入消息,并完成消息发送。*

还有一个问题,我们在聊天过程中,一边在接受别人消息时,一边自己这里还要敲文字,给别人发过去。而在上一篇的执行方案中,程序在执行message = dis.readUTF();时,若是没有服务器向这个客户端发送消息,这条语句就一直处于等待状态,不能输入信息。为此,我们使用线程来处理“侦听来自对端的消息”

同样的,先用接口定义端口号:

package gw.about_net.core;

public interface INet {
	int DEFAULT_PORT = 54187;
}
1.服务器
package gw.about_net.server;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

import gw.about_net.core.INet;

public class SingleClientServer {
	private ServerSocket server;
	private int port;
	
	
	public SingleClientServer() {
		this.port = INet.DEFAULT_PORT;
	}
	
	public SingleClientServer setPort(int port) {
		this.port = port;
		return this;
	}
	
	public void startup() {
		try {
			System.out.println("开始建立服务器......");
			server = new ServerSocket(port);
			System.out.println("服务器建立成功,开始侦听来自客户端的连接......");
			Socket socket = server.accept();
			String clientIp = socket.getInetAddress().getHostAddress();
			System.out.println("侦听到一个客户端[" + clientIp + "]的连接请求,并已连接!");
		
			new Thread(new Communicate(socket)).start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	class Communication implements Runnable{
		private DataInputStream dis;
		private DataOutputStream dos;
		private volatile boolean goon;		//线程标志
		
		public Communication(Socket socket) {
			try {
				dis =new  DataInputStream(socket.getInputStream());
				dos = new DataOutputStream(socket.getOutputStream());
				
				goon = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		public void send(String message) {
			try {
				dos.writeUTF(message);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
			
		@Override
		public void run() {
			//专注于侦听来自客户端的信息
			String message = null;
			while(goon) {
				try {
					message = dis.readUTF();
					System.out.println("来自星星的消息:["+message+"]");
					
					if(message.equalsIgnoreCase("byebye")) {
						goon  = false;
					}else {
						send("["+message+"]");
					}
				} catch (IOException e) {
					if(goon == true) {
						System.out.println("客户端异常掉线!");
					}
				}
			}
			close();
		}
		
		private void close() {
			goon = false;
			try {
				if (dis != null) {
					dis.close();
				}
			} catch (IOException e) {
			} finally {
				dis = null;
			}
			
			try {
				if (dos != null) {
					dos.close();
				}
			} catch (IOException e) {
			} finally {
				dos = null;
			}
			
			try {
				if (server != null && !server.isClosed()) {
					server.close();
				}
			} catch (IOException e) {
			} finally {
				server = null;
			}
		}
	}
}

2.客户端
package gw.about_net.client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import gw.about_net.core.INet;

public class SingleClientClient {
	public static final String DEFAULT_IP = "127.0.0.1";
	
	private int port;
	private String ip;
	private Socket socket;
	
	public SingleClientClient() {
		this.ip = DEFAULT_IP;
		this.port = INet.DEFAULT_PORT;
	}
	
	public SingleClientClient setServerIp(String serverIp) {
		this.ip = serverIp;
		return this;
	}
	
	public SingleClientClient setServerPort(int serverPort) {
		this.port = serverPort;
		return this;
	}
	
	public void ConnectToServer() {
		try {
			socket = new Socket(ip, port);//连接服务器
			
			Communication communication = new Communication(socket);
			new Thread(communication).start();//启动线程
			
			Scanner in = new Scanner(System.in);
			String message = "";
			while(!message.equalsIgnoreCase("byebye")) {
				message = in.next();
				communication.send(message);
			}
			communication.close();
			in.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	class Communication implements Runnable{
		private DataInputStream dis;
		private DataOutputStream dos;
		private volatile boolean goon;
		
		public Communication(Socket socket) {
			try {
				dis = new DataInputStream(socket.getInputStream());
				dos = new DataOutputStream(socket.getOutputStream());
				goon = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		public void send(String message) {
			try {
				dos.writeUTF(message);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
	
		@Override
		public void run() {
			String message = null;
			while(goon) {
				try {
					message = dis.readUTF();
					System.out.println("来自太阳的消息:"+message);
				} catch (IOException e) {
					if(goon == true) {//当goon为true,说明没有主动关闭,即没有接收“byebye”
						System.out.println("服务器异常宕机!");
					}
					goon = false;
				}
			}
			close();
		}
		

		private void close() {
			goon = false;
			try {
				if (dis != null) {
					dis.close();
				}
			} catch (IOException e) {
			} finally {
				dis = null;
			}
			
			try {
				if (dos != null) {
					dos.close();
				}
			} catch (IOException e) {
			} finally {
				dos = null;
			}
			
			try {
				if (socket != null && !socket.isClosed()) {
					socket.close();
				}
			} catch (IOException e) {
			} finally {
				socket = null;
			}
		}
	}
}



运行结果:

客户端:
在这里插入图片描述
服务器端:
在这里插入图片描述

3.通信层

在上述的服务器端与客户端中都有一个方法Communication,用于实现与对端的通信,其中,需要实现的功能是一样的。那么,接下来该怎么做呢?对,就是抽象出来,形成单独的类。

该类负责建立与对端的通信信道,提供发送,接收消息和关闭连接的功能。

package gw.about_net.core;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;


public abstract class Communication implements Runnable {
	protected Socket socket;
	protected DataInputStream dis;
	protected DataOutputStream dos;
	private volatile boolean goon;
	
	//建立与对端的通信信道
	protected Communication(Socket socket) {
		this.socket = socket;
		try {
			this.dis = new DataInputStream(socket.getInputStream());
			this.dos = new DataOutputStream(socket.getOutputStream());
			
			this.goon = true;
			new Thread(this).start();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//发送消息
	public void send(String message) {
		try {
			dos.writeUTF(message);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public  abstract void dealNetMessage(String message) ;	//处理对端消息
	public abstract void peerAbnormalDrop();	//对端异常掉线

	@Override
	public void run() {
		String message = null;
		while(goon) {
			try {
				message = dis.readUTF();
				//需要根据具体消息进行不同处理,给一个抽象方法
				dealNetMessage(message);
			} catch (IOException e) {
				if(goon == true) {
					//根据客户端与服务端的不同,处理对端异常掉线也不同
					peerAbnormalDrop();
					goon = false;
				}
			}
		}
		close();
	}
	
	//关闭连接
	public void close() {
		goon = false;
		try {
			if (dis != null) {
				dis.close();
			}
		} catch (IOException e) {
		} finally {
			dis = null;
		}
		
		try {
			if (dos != null) {
				dos.close();
			}
		} catch (IOException e) {
		} finally {
			dos = null;
		}
		
		try {
			if (socket != null && !socket.isClosed()) {
				socket.close();
			}
		} catch (IOException e) {
		} finally {
			socket = null;
		}
	}
	
}

下面,给一张图,来表示提取出Communication前后通信的接收消息和发送消息端的变化:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值