目录
- 网络编程
- 网络编程三要素
- UDP
- TCP
- 编写UDP的接收端
- 编写UDP的发送端
- 编写TCP的服务端
- 编写TCP的客户端
- 文件上传(多线程)
- commons-io的使用
- Junit
- NIO
- 最后
网络编程
编写在不同计算机上进行数据传输的程序。
网络编程场景
网络应用程序、即时通信、网游对战、金融证券、国际贸易、邮件等等。
常见的软件架构有如下2种形式:Client-Server(CS) 、 Browser/Server(BS)
Client-Server(CS): 客户端-服务端模式
Browser/Server: 浏览器-服务端模式
网络编程三要素
IP地址
IP地址概念
互联网协议地址(Internet Protocol Address),俗称IP。
IP地址用来给网络中的计算机进行编号
IP地址作用
通过IP地址可以找到网络中的某台电脑
IP地址就相当于生活中的家庭住址
IPv4:是一个32位的二进制数,通常被分为4个字节,表示成 a.b.c.d 的形式,例如 192.168.65.100。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。有资料显示,全球IPv4地址在2011年2月分配完毕。为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,分成8组以十六进制数显示,表示成 ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
查看本机IP地址
在DOS命令行输入: ipconfig
检查网络是否连通
在DOS命令行输入: ping IP地址/域名
特殊IP地址
127.0.0.1:是回送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
端口号
端口介绍
我们一台电脑上会安装很多的应用程序,内网通,微信,QQ。如何找到计算机中的某个程序?
应用程序在设备中唯一的标识。
端口作用
通过端口号可以找到电脑上的某个程序
用两个字节表示的整数,它的取值范围是0~65535。
其中0~1023之间的端口号用于一些知名的网络服务或者应用。
我们自己使用1024以上的端口号就可以了。
协议
计算机网络中,连接和通信的规则被称为网络通信协议。
网络通信协议有两套参考模型
OSI参考模型:世界互联协议标准,全球通信规范,单模型过于理想化,未能在因特网上进行广泛推广。
TCP/IP参考模型(或TCP/IP协议):事实上的国际标准。
UDP
UDP协议特点: 用户数据报协议(User Datagram Protocol)
不需要连接
速度快
有大小限制一次最多发送64K
易丢失数据
UDP协议通信场景(速度要求高,数据完整性要求不高)
直播
语音通话
视频会话
TCP
TCP协议特点: 传输控制协议 (Transmission Control Protocol)
需要连接
速度慢
没有大小限制
不易丢失数据
TCP协议通信场景(速度要求不高,数据完整性要求高)
下载
扫码支付
金融等数据通信。
编写UDP的接收端
/*
编写UDP的接收端
DatagramPacket(byte buf[], int length)
创建空的数据包
byte buf[]: 用来保存数据的数组
int length: 数组一次可以接收多少数据
注意:要先运行接收端
*/
public class UDPReceiver {
public static void main(String[] args) throws IOException {
System.out.println("接收端启动!");
// 1.创建接收端
DatagramSocket socket = new DatagramSocket(6666);
// 2.创建空的数据包
byte[] b = new byte[1024];
DatagramPacket packet = new DatagramPacket(b, b.length);
// 3.接收数据, 接收到的数据放到包中
socket.receive(packet);
int length = packet.getLength();
System.out.println(new String(b,0,length));
// 4.关闭资源
socket.close();
}
}
编写UDP的发送端
/*
编写UDP的发送端
DatagramPacket(byte buf[], int offset, int length, InetAddress address, int port)
byte buf[]: 要发送的数据的字节数组
int offset: 从数组哪个位置开始发
int length: 发送多少数据
InetAddress address: 接收方的IP地址
int port: 接收方的端口
InetAddress.getLocalHost(): 获取本机的IP
InetAddress.getByName(""): 别人的IP地址
注意:UDP协议不需要连接,没有接收端也不会报错,数据丢失啦
*/
public class UDPSender {
public static void main(String[] args) throws IOException {
System.out.println("发送端启动!");
byte[] a = "发送".getBytes();
// 1.创建发送端
DatagramSocket socket = new DatagramSocket();
// 2.创建数据包
DatagramPacket packet = new DatagramPacket(a, 0, a.length, InetAddress.getByName("127.0.0.1"), 6666);
// 3.发送数据
socket.send(packet);
// 4.关闭资源
socket.close();
}
}
编写TCP的服务端
public class TCPServer {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动啦!");
// 1.创建TCP服务端
ServerSocket serverSocket = new ServerSocket(8848);
// 2.同意客户端的请求, 如果没有客户端连接就一直等
Socket socket = serverSocket.accept();
// 3.得到输入流读取数据
InputStream inputStream = socket.getInputStream();
byte[] b = new byte[1024];
int len = inputStream.read(b);
System.out.println(new String(b,0,len));
// 4.得到输出流写数据
OutputStream outputStream = socket.getOutputStream();
byte[] b1 = "老地方见".getBytes();
outputStream.write(b1);
// 5.关闭资源
outputStream.close();
inputStream.close();
serverSocket.close();
}
}
编写TCP的客户端
/*
目标: 编写TCP客户端
Socket(String host, int port) 创建客户端, 会自动连接服务端
String host: 服务端的IP
int port: 服务端的端口
注意:
TCP协议先启动客户端,没有服务端,会出现连接异常
TCP程序一定要先运行服务端
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
System.out.println("客户端启动啦!");
// 1.创建客户端
Socket socket = new Socket("127.0.0.1", 8848);
// 2.得到输出流写数据
OutputStream outputStream = socket.getOutputStream();
byte[] a = "约吗".getBytes();
outputStream.write(a);
// 3.得到输入流读取数据
byte[] b = new byte[1024];
InputStream inputStream = socket.getInputStream();
int len = inputStream.read(b);
System.out.println(new String(b,0,len));
// 4.关闭资源
inputStream.close();
outputStream.close();
socket.close();
}
}
文件上传(多线程)
客户端
public class UploadClient1 {
public static void main(String[] args) {
System.out.println("客户端启动");
int i = 0;
while (i<=20) {
i++;
new Thread(()->{
try {
Socket socket = new Socket("127.0.0.1",10086);
FileInputStream fis = new FileInputStream("Study_day12\\abc\\1.png");
OutputStream outputStream = socket.getOutputStream();
int len;
byte[] b = new byte[1024*8];
while((len = fis.read(b))!=-1){
outputStream.write(b,0,len);
}
System.out.println("上传完了");
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
len = inputStream.read(b);
System.out.println("客户端收到的:"+new String(b,0,len));
outputStream.close();
fis.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
UploadRun
public class UploadRun implements Runnable{
private Socket socket;
public UploadRun(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
// 3.得到Socket输入流
InputStream inputStream = socket.getInputStream();
// 4.创建文件输出流
String s = UUID.randomUUID().toString();
FileOutputStream fos = new FileOutputStream("Study_day12\\upload\\"+s+".png");
// 5.循环读写数据
int len;
byte[] b = new byte[1024*8];
//因为这是socket的输入流,是BIO的方式,不会等于-1,只会一直死等,直到别人明确告诉他没人了,不需要等了才会停止
//所以在客户端加上终止等待
while((len = inputStream.read(b))!=-1){
fos.write(b,0,len);
}
// 6.得到Socket的输出流写数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("接收完成".getBytes());
// 7.关闭
outputStream.close();
fos.close();
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端
public class UploadServer1 {
public static void main(String[] args) throws IOException {
System.out.println("文件上传服务端启动啦!");
ExecutorService pool = Executors.newFixedThreadPool(10);
// 1.创建服务端
ServerSocket serverSocket = new ServerSocket(10086);
while (true){
// 2.同意客户端的连接
Socket socket = serverSocket.accept();
UploadRun uploadRun = new UploadRun(socket);
pool.submit(uploadRun);
}
}
}
commons-io的使用
public static void main(String[] args) throws IOException {
// 1.读取文件中内容返回字符串
String s = FileUtils.readFileToString(new File("Study_day12\\abc\\bw.txt"), "utf8");
System.out.println(s);
// 2.复制文件(复制的文件必须存在,复制后的路径不存在会自动生成)
FileUtils.copyFile(new File("Study_day12\\abc\\bw.txt"),new File("Study_day12\\aaa\\bw.txt"));
// 3.复制文件夹,放到指定目录下
FileUtils.copyDirectoryToDirectory(new File(""),new File(""));
}
}
Junit
/*
Junit的使用步骤:
1.定义测试方法
2.在方法上添加@Test注解
3.运行测试方法
单元测试方法的要求 *****
1.必须是public修饰
2.返回值必须是void
3.不能有参数
4.不能是静态方法
*/
public class TestFirst {
@Test
public void test(){
}
}
/*
@Before:在每个测试的方法前运行。
@After:在每个测试的方法后运行。
@BeforeClass:在所有测试的方法前运行一次。
@AfterClass:在所有测试的方法后运行一次。
资源初始化
*/
public class TestSecond {
@Test
public void test1(){
System.out.println("单元测试1");
}
@Test
public void test2(){
System.out.println("单元测试1");
}
//需要加静态
@BeforeClass
public static void BeforeClass(){
System.out.println("BeforeClass");
}
@AfterClass
public static void AfterClass(){
System.out.println("AfterClass");
}
@Before
public void Before(){
System.out.println("Before");
}
@After
public void After(){
System.out.println("After");
}
}
NIO
NIO概述
JDK1.4以前: InputStream/OutputStream称为BIO(Blocking IO) 阻塞式IO
JDK1.4推出了一套新的IO体系称为NIO (New IO/ Not Blocking IO) 非阻塞式IO
阻塞、非阻塞概念
阻塞和非阻塞都是处理IO数据的方式
阻塞:如果没有数据就一直等待
非阻塞:如果没有数据,不会一直等待,可以做其他事情
非阻塞的好处,不需要一直等待,当有数据来才需要处理,没有数据可以做其他操作
Channel(通道)可以双向传输数据
ByteBuffer 相当于之前BIO的byte[],可以保存要发送和接收的数据
ByteBuffer 效率比byte[]要高,功能更强大
使用了多路复用,只需要一个线程就可以处理
多个通道,降低内存占用率,减少CPU切换时间,
在高并发、高频段业务环境下有非常重要的优势
NIO服务端:
1.使用Selector多路复用器,监听每个客户端的请求事件。
2.服务端不会阻塞等待某个客户端。
NIO 有对应的框架netty
tomcat服务器底层也是NIO
最后
如果你对本文有疑问,你可以在文章下方对我留言,敬请指正,对于每个留言我都会认真查看。