链接: https://download.csdn.net/download/qq_39150049/12557360.
本文将介绍一下如何通过Socket连接服务器,同时还将借此机会介绍一下封装过程,为了能够更好的介绍,本次采用递进的方式进行介绍。因此,我将分为封装前和封装后两个部分来介绍。正好借此机会,也介绍一下单例,properites和C/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存在的意义。由于IP等参数有可能改动,但是因为一些参数的改动就需要改动代码,对于用户来说这不是一件简单的事。所以有了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、隐藏了实现细节
/**
*
* @author T_Antry
* @describe 服务器测试类
* ServerSocket
* 2020年6月27日
*/
public class MainServerTest {
/**
* @param args
* @describe 运行服务器
*/
public static void main(String[] args) {
ServerNet.getServerNet().runServer();//运行服务器
}
}
/**
*
* @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();
}
}
}
/**
*
* @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();
}
}
}
/**
*
* @author T_Antry
* @describe 客户端测试类
* ClientSocket
* 2020年6月27日
*/
public class MainClientTest {
public static void main(String[] args) {
ClientNet.getClientNet().write("Hello World!");//发送一行消息便会首次创建仅此一个连接对象
}
}
/**
*
* @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();
}
}
/**
*
* @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();
}
}
}
}