Socket连接(java)

案例下载

链接: https://download.csdn.net/download/qq_39150049/12557360.

Socket连接案例(java)

本文将介绍一下如何通过Socket连接服务器,同时还将借此机会介绍一下封装过程,为了能够更好的介绍,本次采用递进的方式进行介绍。因此,我将分为封装前和封装后两个部分来介绍。正好借此机会,也介绍一下单例properitesC/S的内容,至于json和服务部分,本次案例裁剪掉,希望在下次的分享中可以介绍。

初步连接

此次介绍采用Client/Server架构进行交互,需要创建连接,首先当然是先创建一个服务器,下面我将一步一步带着大家简单建立基本服务器。

如何创建服务器?

建立之前,讲两句废话。至于底层的网卡怎么发送包我就不讲了。我提一下端口IP,相对来讲IP大家听得会多一点。假设IP是蜂巢自助快递柜的话,我就把端口比喻成柜上的啥,格子编号吧!一条消息通过IP地址送到了柜子上,再放到具体端口中。那每个应用程序都有自己对应的端口,如果端口上有消息就可以去读取。

下面展示一些 内联代码片

/**
 * 
 * @author T_Antry
 * @describe 封装前服务器
 * ServerSocket
 * 2020年6月27日
 */
public class primaryTest {
	/**
	 * 
	 * @param args
	 * @describe 这里只处理一次连接的收发,随后主程序结束
	 */
	public static void main(String[] args) {
		final int PORT = 10086;/*计算机端口数量有65535,我们一般使用一万之后的端口*/
		ServerSocket server = null;//net包下的一个类,用于创建服务器
		Socket socket = null;//net包下的一个类,用于处理连接事务
		PrintWriter writer = null;
		BufferedReader reader = null;
		try {
			/*建立连接*/
			server = new ServerSocket(PORT);//感叹一下java确实牛逼,创建个服务器,就这样创建个对象,OK了,
			System.out.println("======Server======");
			System.out.println("Wait connect...");
			socket = server.accept();//当由client连接时,可以获取到一个socket连接,当然这个链接是个阻塞的方法,如果没有用户连接,它就会一直停在这里
			System.out.println("Connect success");
			/*从socket中,我们可以获取到输入输出流*/
			writer = new PrintWriter(socket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			/*现在我们就可进行简单的消息收发了*/
			String msg = reader.readLine();//连接好之后读一行消息
			System.out.println("Server received :"+msg);
			if(msg.equals("我是T_Antry"))//假设这是连接暗号
			{
				writer.println("暗号正确,你是我的宝贝");
			}
			else
			{
				writer.println("暗号错误,你是个大傻逼");
			}
			writer.flush();//这个方法必不可少,暂且可以理解为发送键
			/*首个简单实例,到这边连接就断开程序结束*/
			System.out.println("Server close");
			System.out.println("================");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(reader!=null)
					reader.close();
				if(writer!=null)
					writer.close();
				if(socket!=null)
					socket.close();
				if(server!=null)
					server.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
	}
}
如何创建客户端?

上面我们的服务器用的端口 10086,那我们现在去连接这个服务器,有了端口号,还需要知道服务器的IP,因为我们是在本机上进行测试,所以IP可以使用127.0.0.1,至于为什么是这个,那还是百度,哈哈哈
口号,还需要知道服务器的IP,因为我们是在本机上进行测试,所以IP可以使用127.0.0.1,至于为什么是这个,那还是百度,哈哈哈

下面展示一些 内联代码片

/**
 * 
 * @author T_Antry
 * @describe 封装前客户端
 * ClientSocket
 * 2020年6月27日
 */
public class PrimaryTest {
	/**
	 * 
	 * @param args
	 * @describe
	 */
	public static void main(String[] args) {
		final int PORT = 10086;//服务器的端口
		final String IP = "127.0.0.1";//服务器的IP
		Socket socket = null;//net包下的一个类,用于处理连接事务
		PrintWriter writer = null;
		BufferedReader reader = null;
		System.out.println("======Client======");
		System.out.println("Connecting...");
		try {
			/*通过IP,和端口进行连接*/
			socket = new Socket(IP, PORT);
			System.out.println("Connect success");
			/*从socket中,我们可以获取到输入输出流*/
			writer = new PrintWriter(socket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			/*现在可以收发消息了*/
			writer.println("我是T_Antry");
			writer.flush();//这个方法必不可少,暂且可以理解为发送键
			String msg = reader.readLine();//读取一行消息
			System.out.println("Client received :"+msg);
			/*首个简单实例,到这边连接就断开程序结束*/
			System.out.println("Client close");
			System.out.println("================");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(reader!=null)
					reader.close();
				if(writer!=null)
					writer.close();
				if(socket!=null)
					socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
	}
}
运行结果

注意,先运行服务器,如果服务器没有先运行,客户端连接不上服务器会报异常
在这里插入图片描述
可以看到,客户端连接上服务器后收到一段消息
在这里插入图片描述
客户端也受到了服务器的回复。

properties使用的介绍

首先简要讲一下,properties存在的意义。由于IP等参数有可能改动,但是因为一些参数的改动就需要改动代码,对于用户来说这不是一件简单的事。所以有了properties配置文件,通过配置文件去修改,显得人性化很多。

properties创建

一般是在工程文件目录下创建properties文件夹,再创建相应的properties文件。文件中只需按如图所示写入键值对即可,“=”的左边为键,右边为值
在这里插入图片描述
这边展示一下客户端代码的改动,服务器端同理。
下面展示一些 内联代码片

/**
 * 
 * @author T_Antry
 * @describe 封装前客户端
 * ClientSocket
 * 2020年6月27日
 */
public class PrimaryTest {
	/**
	 * 
	 * @param args
	 * @describe
	 */
	public static void main(String[] args) {
		/*------------------------------------------------------------------*/
		Properties pro = new Properties();//创建一个properties对象
		try {
			pro.load(new FileInputStream("properties/net.properties"));//载入properties文件,参数为路径
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*从properties中获取连接参数*/
		int port = Integer.valueOf(pro.getProperty("PORT"));//服务器的端口,参数为键
		String ip =(String)pro.getProperty("IP");//服务器的IP,参数为键
		/*------------------------prioperties改动-----------------------*/
		Socket socket = null;//net包下的一个类,用于处理连接事务
		PrintWriter writer = null;
		BufferedReader reader = null;
		System.out.println("======Client======");
		System.out.println("Connecting...");
		try {
			/*通过IP,和端口进行连接*/
			socket = new Socket(ip, port);
			System.out.println("Connect success");
			/*从socket中,我们可以获取到输入输出流*/
			writer = new PrintWriter(socket.getOutputStream());
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			/*现在可以收发消息了*/
			writer.println("我是T_Antry");
			writer.flush();//这个方法必不可少,暂且可以理解为发送键
			String msg = reader.readLine();//读取一行消息
			System.out.println("Client received :"+msg);
			/*首个简单实例,到这边连接就断开程序结束*/
			System.out.println("Client close");
			System.out.println("================");
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(reader!=null)
					reader.close();
				if(writer!=null)
					writer.close();
				if(socket!=null)
					socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
	}
}

封装前,先介绍一下单例

什么是单例

单例模式,是一种常用的软件设计模式,通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。

那么服务器,客户端为什么要用到单例?

因为我们实际应用中,服务器只有一个,同时,这个客户端连接也只希望它与同一个服务器只有一次连接,为了避免出现每次发送信息都创建一次连接等其它问题,我们采用了单例的思想。

具体的代码实现,后面的代码我将会详细注释,希望你可以看懂,当然也可以私聊我,嘿嘿

为什么要封装?

1、提高了安全性
2、提高了复用性
3、隐藏了实现细节

服务器部分

Test类
/**
 * 
 * @author T_Antry
 * @describe 服务器测试类
 * ServerSocket
 * 2020年6月27日
 */
public class MainServerTest {
	/**
	 * @param args
	 * @describe 运行服务器
	 */
	public static void main(String[] args) {
		ServerNet.getServerNet().runServer();//运行服务器
	}
}
Net类
/**
 * 
 * @author T_Antry
 * @describe 服务器类
 * ServerSocket
 * 2020年6月27日
 */
public class ServerNet {
	/*Member*/
	private static ServerNet serverNet;//单例的对象
	private ServerSocket server = null;//net包下的一个类,用于创建服务器
	private static int count = 0;//用于计数连接次数
	/*Constructor*/
	/**
	 * @构造 这里的构造方法为私有,目的就是防止外部创建对象
	 */
	private ServerNet()
	{
		Properties pro = new Properties();//创建一个properties对象
		try {
			pro.load(new FileInputStream("properties/net.properties"));//载入properties文件,参数为路径
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/*从properties中获取连接参数*/
		int port = Integer.valueOf(pro.getProperty("PORT"));//服务器的端口,参数为键
		try {
			server = new ServerSocket(port);//创建个服务器。
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/*Method*/
	/**
	 * @return ServerNet对象
	 * @describe 获取单例对象,如果对象还未创建,则创建后返回对象
	 */
	public static ServerNet getServerNet()
	{
		if(serverNet==null)
			serverNet = new ServerNet();
		return serverNet;
	}
	/**
	 * 
	 * 
	 * @describe
	 */
	public void runServer()
	{
		try {
			System.out.println("Wait connect...");
			Socket socket = server.accept();//当client连接时,可以获取到一个socket连接,当然这个链接是个阻塞的方法,如果没有用户连接,它就会一直停在这里
			System.out.println("Connect account :"+(++count));
			new Thread(new ServerRunnable(socket)).start();//开启线程,因为等待消息是一个阻塞的方法,一个服务器都要支持多连接,因此每个连接都需要开一个线程去接收消息
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
Runnable类
/**
 * 
 * @author T_Antry
 * @describe 服务器连接线程
 * ServerSocket
 * 2020年6月27日
 */
public class ServerRunnable implements Runnable{
	/*Member*/
	private Socket socket;
	private PrintWriter writer;
	private BufferedReader reader;
	/*Constructor*/
	public ServerRunnable(Socket socket) {//传入socket连接
		super();
		this.socket = socket;
	}
	@Override
	public void run() {
		try {
			/*获取输入输出流*/
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			writer = new PrintWriter(socket.getOutputStream());
			while(true)//原本这个循环中做的是获取一行消息,拿去解析并跳到相应服务中,本次不做介绍,将在下一次章节中介绍
			{
				System.out.println("-----------------");
				String msg = reader.readLine();//读一条
				System.out.println("Server receive :"+msg);
				Thread.sleep(5000);//休眠五秒后
				writer.println("T_Antry服务器");
				writer.flush();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

客户端部分

Test类
/**
 * 
 * @author T_Antry
 * @describe 客户端测试类
 * ClientSocket
 * 2020年6月27日
 */
public class MainClientTest {
	public static void main(String[] args) {
		ClientNet.getClientNet().write("Hello World!");//发送一行消息便会首次创建仅此一个连接对象
	}
}
Net类
/**
 * 
 * @author T_Antry
 * @describe 
 * ClientSocket
 * 2020年6月27日
 */
public class ClientNet {
	/*Member*/
	private static ClientNet clientNet;
	private PrintWriter writer;
	/*Constructor*/
	/**
	 * @构造 这里的构造方法为私有,目的就是防止外部创建对象
	 */
	private ClientNet() {
		Properties pro = new Properties();//创建一个properties对象
		try {
			pro.load(new FileInputStream("properties/net.properties"));//载入properties文件,参数为路径
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		/*从properties中获取连接参数*/
		int port = Integer.valueOf(pro.getProperty("PORT"));//服务器的端口,参数为键
		String ip =(String)pro.getProperty("IP");//服务器的IP,参数为键
		/*通过IP,和端口进行连接*/
		try {
			System.out.println("======Client======");
			System.out.println("Connecting...");
			Socket socket = new Socket(ip, port);
			new Thread(new ClientRunnable(socket)).start();//开启线程
			System.out.println("Connect success");
			/*从socket中,我们可以获取到输入输出流*/
			writer = new PrintWriter(socket.getOutputStream());
			
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static ClientNet getClientNet()
	{
		if(clientNet==null)
			clientNet = new ClientNet();
		return clientNet;
	}
	/**
	 * 
	 * @param msg
	 * @describe 发送消息
	 */
	public void write(String msg)
	{
		writer.println(msg);
		writer.flush();
	}
}

Runnable类
/**
 * 
 * @author T_Antry
 * @describe 客户端接收线程
 * ClientSocket
 * 2020年6月27日
 */
public class ClientRunnable implements Runnable{
	private Socket socket;
	private BufferedReader reader = null;
	public ClientRunnable(Socket socket) {
		this.socket = socket;
	}
	@Override
	public void run() {
		try {
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			while(true)//原本这个循环中做的是获取一行消息,拿去解析并跳到相应服务中,本次不做介绍,将在下一次章节中介绍
			{
				System.out.println("-----------------");
				String msg = reader.readLine();
				System.out.println("Client received :"+msg);
				ClientNet.getClientNet().write("Hello Word!");
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
		
				try {
					if(reader!=null)
						reader.close();
					if(socket!=null)
						socket.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
}

运行结果

Server

在这里插入图片描述

Client

在这里插入图片描述
每隔5秒便会交互一次。

那本次的介绍就到这边了,关于包结构,json,service部分的内容后续将会更新,如果觉得还算满意的话,点个赞吧!

猜你喜欢

转载自blog.csdn.net/qq_39150049/article/details/106984237