package cn.webSocket.service;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
* 今天整理以下网络编程的基础Demo;
*
* 网络编程:
*
* 1. 软件架构:
* BS: Browser/Server(浏览器/服务器)
* CS: Client/Server(客户端/服务器)
*
* 2. 网络通信三要素:
*
* 2.1. 网络通信协议:
* 计算机必须遵守的协议,其中对数据的传输格式,传输速率,传输步骤等做了相应的规范,只有遵守相应的规则计算机之间才能进行准确无误的交互;
* (TCP/IP协议:传输控制协议/因特网互联协议(Transmission Control Protocol/Internet Protocol
* TCP协议是面向连接的通信协议:意思是说在数据传递前,会首先建立发送端和接收端之间的逻辑连接,然后进行数据传输;
* 目前TCP能够极大程度上提供两台计算机之间可靠无差别的数据传输;其主要可靠性体现在数据交互前的准备阶段:
* 第一阶段:客户端向服务端发送连接请求,等待服务器确认;
* 第二阶段:服务器端回应客户端,收到连接请求;
* 第三阶段:客户端再次向服务端发送确认信息,服务器收到,确认连接成功;
* 以上的三次连接信息交互保证了数据传输的可靠性;完成上面的连接验证后,就是正式的数据交互;由于TCP协议在数据传输上的安全性能显著,
* 目前应用的场景较为广泛,如文件下载,网页浏览等...
*
* UDP协议:用户数据报协议(User Datagram Protocol);UDP协议是面向无连接的一种协议,其特点是:数据传输时,无需事先建立与服务器的通信连接,
* 不管服务器是否启动成功,直接将数据,数据源和目的地封装进入一个限定64K以内大小的数据包中然后发送;这种协议其实是一种不安全的协议;
* 它存在的缺点就是它的不可靠性.虽然因没有了预连接使其传输速度相对加快,但是容易丢失数据.所以在日常的应用中主要是应用在视频会议或语音聊天等地方;
*
* );
*
* 协议规范了不同设备如何连入因特网,以及相互之间如何数据传递的规则;
* 其内部包含了一系列用户数据通讯处理的各种协议,并且采用的4层的分层模型,每一层都会呼叫下一层提供的协议来完成自己的需求;)
*
* 2.2. IP地址:用来记录每一台设备的唯一标识;
* 起源:IP地址早期只有IPv4,是一个32位的二进制数,通常被分为4个字节,以a.b.c.d的形式组成;例如192.168.31.1;
* 其中的a,b,c,d都是0~255之间的十进制整数,大约最多为42亿个;在2011年2月的数据统计中,IPv4的IP已全数分配完;
* 后来:为了扩大地址空间,重新定义了IPv6的地址空间,采用128位地址长度,每16个字节一组,分成8组16进制数来表示;
* 如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789;后来的统计成IPv6的方式分配IP可以为地球上的每一粒沙子编著一个地址;
*
* 2.3. 端口号
* 网络通信本质上是不同应用程序间的两个进程之间的通信;为了区分计算机中各自的进程,就会用到端口来区分;
* IP标注网络中不同的设备,而端口号则是标示设备中不同进程;
* 端口号:使用两个字节表示的整数,取值范围在0~65535之间;其中0~1023的端口大都会被知名的网络服务和应用占取,普通的应用程序则可以从
* 1024之后取.如果端口取值已被其他进程占据,则新的程序将无法启动,除非解除对应的端口占用;
*
* 也就是说:通过网络协议+IP地址+端口号,就可以区别网络中不同设备,进程的交互;
*
* 3. TCP通讯规则:
* 3.1. 服务端程序需要预先启动来等待客户端程序的请求;
* 3.2. 客户端主动连接成功服务器端方可进行通讯,而服务器端不可以主动连接客户端;
* 3.3. java.net包中提供了两种常见协议的支持(TCP,UDP)用来TCP通讯,里面提供了一些低层次的通信细节;其中会用到两个主要的类java.net.Socket|java.netServiceSocket;
* java.net.Socket:客户端(封装客户端套接字);
* java.netServiceSocket:服务端;
*
* 好,接下来就开始模拟一下关于Java中Socket通讯的Demo示例;
*
* 在这里说明一下,起始的时候没有搞明白Socket到底是个什么东西,既然通讯需要客户端和服务器,那是不是我也需要拥有一个服务器和本地的开发机来配合;
* 才能够完成我的Socket通讯测试.直到后来经过多番研习后才明白其中的奥义.希望能够通过下面的Demo来清晰的展现Socket通讯面纱后的真容;
*
* 整体的还是和以往一样,首先创建自己的测试类:
* 但是这里也说明一下,因为我们是客户端和服务器的交互,所以必定有两个独立的程序来发送各自的请求与响应,那么既然是两个独立的程序,而且各自还有自己
* 独立的信息需要请求和发送,那么各自必定需要有一个类似main函数的程序执行入口来发起自己的数据;
*
* 而且Socket通讯的核心也就是网络变成的核心:通讯协议+IP地址+端口号;
*
* Socket和ServiceSocket是两个终端各自封装的底层通讯类,故而必然是两个执行的入口;
*
* 大意如上,或有不清晰之处,多加思量...抱拳!
*
* 好 下面的类为服务器端模拟测试类;
* @author TANJIYUAN
*
*/
public class Service {
/**
* 程序入口|主函数;
*
* 在这里阐述一下大致的通信导向:
*
* 1. 服务端 启动,创建ServerSocket对象,等待连接;
* 2. 客户端 启动,创建Socket对象,请求连接;
* 3. 服务端 接收连接,通过accept()方法监听获取客户端Socket对象;
* 4. 客户端 通过Socket对象获取对应的输出流,向服务端发送数据;
* 5. 服务端 通过获取的客户端Socket对象获取对应的输入流来读取客户端通过输出流发送的数据;
* 6. 服务端 接受到数据处理后通过获取到的客户端Socket对象获取对应的输出流,来向客户端回写数据;
* 7. 客户端 通过Socket对象获取输入流来读取服务端回写的数据内容;
* 8. 资源释放;连接断开;
*
* @param args
*/
public static void main(String[] args) {
try {
/**
* 打印流
* 通过打印流改变打印数据的输出位置;
* -------------------------------------------------------------------------
* 这里说明一下:
* 如果直接在控制台答应,无法全量看到Service服务器端和Client客户端的打印数据;
* 所以引入以一个打印流,将数据内容调转一个自定义地址进行数据信息打印;
* 此处初始化一个数据打印地址;
*/
String path = "D://Service.txt";
// 初始化一个打印流; 控制编码;
PrintStream ps = new PrintStream(path, "UTF-8");
// 改变打印流的位置,指定自定义打印流;
System.setOut(ps);
// -------------------------------------------------------------------------
// 实例化服务器端Socket对象;指定服务器程序的进程端口号(绑定端口);
ServerSocket server_socket = new ServerSocket(65534);
System.out.println("服务端启动... Weit Conn...");
/**
* 通过accept()方法获取客户端Socket对象;
* accept()方法会一直处于阻塞状态实时监听,直到获取到Socket对象为止;
*/
Socket accept = server_socket.accept();
// 获取客户端Socket对象的输出流数据,得到一个数据输入流对象;
InputStream inputStream = accept.getInputStream();
// 封装一个自定义缓存数组;
byte [] bytArr = new byte[8*1024];
// 读取客户端Socket对象的流数据;(输出流转输入流数据)
int index = inputStream.read(bytArr,0,bytArr.length);
// 输出打印数据;
System.out.print(new String(bytArr,0,index));
// 获取客户端Socket对象的输出流对象;
OutputStream outputStream = accept.getOutputStream();
// 向输出流装在数据;
outputStream.write("En En Welcome Client......I'm Service".getBytes());
// 资源释放;
outputStream.close();
ps.close();
inputStream.close();
accept.close();
server_socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.webSocket.client;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
/**
* 以下是客户端模拟测试类;
*
* 响应的引文可以在服务器端测试类上的注释中进行查看...
*
* @author TANJIYUAN
*
*/
public class Client {
/**
* 程序的入口|主函数;
* @param args
*/
public static void main(String[] args) {
try {
/**
* 打印流
* 通过打印流改变打印数据的输出位置;
* -------------------------------------------------------------------------
* 这里说明一下:
* 如果直接在控制台答应,无法全量看到Service服务器端和Client客户端的打印数据;
* 所以引入以一个打印流,将数据内容调转一个自定义地址进行数据信息打印;
* 此处初始化一个数据打印地址;
*/
String path = "D://Client.txt";
// 初始化一个打印流; 控制编码;
PrintStream ps = new PrintStream(path, "UTF-8");
// 改变打印流的位置,指定自定义打印流;
System.setOut(ps);
// -------------------------------------------------------------------------
/**
* 初始化客户端Socket对象;指定请求IP以及端口号;
* 需要注意:端口号是使用两个字节表示的整数,取值范围在0~65535之间;
*/
Socket socket = new Socket("127.0.0.1",65534);
System.out.println("客户端启动... Will Conn...");
// 获取客户端Socket对象的输出流;
OutputStream outputStream = socket.getOutputStream();
// 装在流数据;
outputStream.write("Hello Server: I'm Client Client Client...".getBytes());
// 获取Socket对象的流数据;(读取服务器端请求到的输出流数据)
InputStream inputStream = socket.getInputStream();
// 封装一个缓存数组;
byte [] charArr = new byte [8*1024];
// 读取流数据;
int index = inputStream.read(charArr);
// 输出打印数据;
System.out.print(new String(charArr,0,index));
// 资源释放;
outputStream.close();
inputStream.close();
ps.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.webSocket.service;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 2.0: 服务端
*
* WebSocket数据文件Demo;
*
* 下面走一个小的数据数据文件交互Demo;
*
* 同上面的样例规则一样,我们分服务器端和客户端;
*
* 那么首先创建一个服务器端的测试类"Service";
* @author TANJIYUAN
*
*/
public class Service {
/**
* 程序入口|主函数;
*
* @param args
*/
public static void main(String[] args) {
try {
// 实例化指定端口的服务器端Socket对象;
ServerSocket serverSocket = new ServerSocket(65530);
/**
* PrintStream;
*
* 通过打印流改变数据输出方向;
* ------------------------------------------------------------------
* 首先初始化一个输出地址;
*/
String path = "D://Server_Upload.txt";
// 实例化打印流,并指定对应的编码和输出地址;
PrintStream ps = new PrintStream(path, "UTF-8");
// 指定自己的打印流;
System.setOut(ps);
// ------------------------------------------------------------------
// 数据打印;
System.out.println("Server start Ok ...");
// 通过accept()方法实时监听获取客户端Socket对象;
Socket clientSocket = serverSocket.accept();
// 实例化一个缓冲byte数组;
byte [] byteArr = new byte [8*1024];
/**
* getInputStream():通过获取到的客户端Socket对象获取客户端Socket对象的输入流;
*
* 通过指定客户端输入流实例化到一个缓冲输入流中;
*/
BufferedInputStream bis = new BufferedInputStream(clientSocket.getInputStream());
// 初始化一个数据输出位置;
String WritePath = "D://"+System.currentTimeMillis()+".png";
// 实例化一个缓冲输出流;
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(WritePath));
// 预定义一个记录缓冲流读取源数据的标位;
int length = 0;
// 缓冲流循环读取源数据;
while((length = bis.read(byteArr)) != -1) {
// 输出流写入数据;
bos.write(byteArr, 0, length);
}
// 提交一次之前写入的数据;
bos.flush();
// 再次写入数据;
bos.write("Data upload Over! ".getBytes());
// 资源释放;
bos.close();
bis.close();
clientSocket.close();
ps.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
package cn.webSocket.client;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
/**
*
* 2.0: 客户端;
*
* 下面是客户端的模拟测试类;
*
* @author TANJIYUAN
*
*/
public class Client {
/**
* 程序的入口|主函数;
* @param args
*/
public static void main(String[] args) {
try {
/**
* PrintStream;
*
* 通过打印流改变数据输出方向;
* -------------------------------------------
* 初始化一个输出地址;
*/
String logPath = "D://Client.txt";
// 实例化打印流,并指定对应的编码和输出地址;
PrintStream ps = new PrintStream(logPath,"UTF-8");
// 指定自己定义的打印流;
System.setOut(ps);
// -------------------------------------------
// 实例化客户端Socket对象,指定请求的服务器IP和对应服务器上的进程端口;
Socket socket = new Socket("127.0.0.1",65530);
// 数据打印;
System.out.println("Socket start Over! ");
// 初始化数据源;
String path = "D://30139986c0adc88a76de14b375e0531.png";
// 实例化输入流;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));
// 实例化一个byte缓冲数组;
byte [] charArr = new byte [8*1024];
// 通过客户端Socket对象获取输出流;
OutputStream outputStream = socket.getOutputStream();
// 预定义一个记录源数据批量读取的最后位标;
int length = 0;
// 循环读取源数据;
while((length = bis.read(charArr)) != -1) {
// 数据写入;
outputStream.write(charArr, 0, length);
}
// 数据提交;
outputStream.flush();
// 再次写入;
outputStream.write("Socket send Over! ... ".getBytes());
// 资源释放;
outputStream.close();
bis.close();
socket.close();
ps.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param bytes
* @return
*/
public static byte[] getBytes(char[] chars) {
Charset cs = Charset.forName("UTF-8");
CharBuffer cb = CharBuffer.allocate(chars.length);
cb.put(chars);
cb.flip();
ByteBuffer bb = cs.encode(cb);
return bb.array();
}
/**
*
* @param bytes[]
* @return
*/
public static char[] getChars(byte[] bytes) {
Charset charSet = Charset.forName("UTF-8");
ByteBuffer bb = ByteBuffer.allocate(bytes.length);
bb.put(bytes);
bb.flip();
CharBuffer cb = charSet.decode(bb);
return cb.array();
}
}