网络编程入门
网络编程概述
计算机网络
计算机网络是指地理位置不同的具有独立功能的计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理协调下,实现资源共享和信息传递的计算机系统。
(客户端和服务器通过互联网连接起来实现资源共享和信息传递)
网络编程
网络编程
在网络通信协议下,实现网络互联的不同计算机上运行的程序间可以进行数据交换。
网络编程的三要素
IP地址
要想让网络中的计算机能够相互通信,必须为每台计算机指定一个标识号,通过这个标识号来指定接受数据的计算机和识别发送的计算机,IP地址就是标识号,也就是设备的标识。
端口:端口号是可以唯一表示设备中的应用程序
协议:通过计算机网可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则。在计算机网络中,这些连接和通信的规则被称为网络通信协议,他对数据的传输格式,传输速率,传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换,常见的协议有UDP协议和TCP协议。
IP地址
网络中设备的唯一标识
IP地址分两大类
-
IPv4
是给每个连接在网络上的主机分配一个32bit的地址,按照TCP/IP规定,ip地址用二进制来表示是,每个地址 长32bit,也就是四个字节.为了使用方便,IP地址常被写成十进制的形式,中间有都好分割不同的字节,这种表示方法叫做点分十进制表示法。
-
IPv6
由于互联网发展,IP地址需求量增大,网络地址数量有限,为了扩大地址空间,通过IPv6重新定义地址空间,采用128位的地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
常用命令
ipconfig:查看本机IP地址
ping ip地址:检查网络是否连通
特殊的IP地址
127.0.0.1:是霍总地址,可以代表本机地址,一般用来测试
InetAddress的使用
InetAddress表示Internet协议(IP)地址
使用方式代码如下
public static void main(String[] args) throws UnknownHostException {
InetAddress address = InetAddress.getByName("192.168.137.1");
//获取IP地址的主机名
String name = address.getHostName();
//返回文本显示的IP地址字符串
String hostAddress = address.getHostAddress();
System.out.println("主机名:"+name);
System.out.println("ip地址:"+hostAddress);
}
端口
端口:设备上应用程序的唯一标识
端口号:用两个字节表示的整数,他的取值范围是065535.其中,01023之间的端口号用于一些知名
网络服务和应用,普通应用程序需要使用1024以上的端口号。如果端口号被另一个服务或应用占用。会导致当前程序启动失败。
协议
协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议
-
用户数据报协议(User Datagram Protocol)
-
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另一台计算机发送数据时,发送端不会去确认接收端是否存在,就会发送出数据,同样接受端在接受到数据是,也不会向发送端反馈是否收到数据
由于使用UDP协议消耗较小,通信效率高,所以通常会用于音频,视频和普通数据的传输。
-
例如视频会议一般采用UDP协议,因为这种情况即使偶尔丢失一两个数据包。也不会对数据结果产生影响。但是在使用UDP协议传输数据时,由于UDP协议的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。
TCP协议
- 传输控制协议(Transmission Control Protocol)
- TCP协议是面向连接的协议,即传输数据之前,在发送端和接收端建立逻辑联系,然后再传输数据。它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器,由客户端向服务端发出连接请求,每次连接的创建都需要经过三次握手。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠性。
- 第一次握手:客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手:服务器端向客户端会送一个响应 ,通知客户端收到了连接请求
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接
- 完成 三次握手,建立连接后,客户端和服务器端可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全性,所以应用十分广泛,例如上传文件,下载文件,浏览网页等。
UDP通信程序
UDP通信原理
UDP协议是一种不可靠的网络协议,它在网络的两端个简历一个Socket对象.但是这两个Socket只是发送,接受数据的对象.因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念.
Java 提供了DatagramSocket类作为基于UDP协议的Socket.
UDP发送数据
发送数据的步骤
- 创建发送端的Socket对象(DatagramSocket)
- 创建数据并把数据打包
- 调用DatagramSocket对象发送数据
- 关闭发送端
public static void main(String[] args) throws IOException {
//创建Socket对象
DatagramSocket ds=new DatagramSocket();
//创建数据,并把数据打包
//DatagramPacket(byte[] buf, int length, InetAddress address,
// int port) 构造一个数据包,
// 发送长度为 length的数据包到指定主机上的指定端口号。
byte[] bys="超级喜欢菲菲".getBytes();
int length=bys.length;
InetAddress inetAddress = InetAddress.getByName("192.168.37.1");
int port=10087;
DatagramPacket dp=new DatagramPacket(bys,length,inetAddress,port);
//调用DatagramSocket对象的方法发送数据
//void send(DatagramPacket p) 从此套接字发送数据报包。
ds.send(dp);
//关闭发送端
//void close() 关闭此数据报套接字。
ds.close();
}
UDP接收数据
UDP接收数据的步骤
- 创建接收数据端DatagramSocket对象
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台上显示
- 关闭接收端
TCP通信程序
TCP通信协议是一种可靠的网络协议,他在通信的两端个简历一个Socket对象,从而在通信的两端形成虚拟链路。
一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟的链路进行通信。
Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表连段的通信端口,并通过Socket产生的IO流来进行网络通信
Java为客户端童工了Socket类,为服务提供了ServerSocket类
TCP发送数据
发送数据步骤
- 创建客户端的Socket对象
- 获取输出流,写数据
- 释放资源
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
//Socket()InetAddress address.int port)创建流套接字并将其链接到指定的IP地址的指定端口号
//Socket s=new Socket(InetAddress.getByName("192.168.43.240"),10001);
Socket s=new Socket("192.168.43.1",10086);
//获取数据流,写数据
OutputStream os = s.getOutputStream();
os.write("Mulujet".getBytes());
//释放资源
s.close();
}
TCP接收数据
接受数据的步骤
-
创建服务器端的Socket对象
-
监听客户端连接,并返回一个Socket对象
-
获取输入流,读数据,并把数据显示在控制台
-
释放资源
public static void main(String[] args) throws IOException { //创建服务区端Socket对象 ServerSocket ss=new ServerSocket(10086); //监听发送端 Socket s = ss.accept(); //读数据,并显示在控制台 InputStream is = s.getInputStream(); byte [] bys=new byte[1024]; int len=is.read(bys); String data=new String(bys,0,len); System.out.println("数据是:"+data); //释放资源 s.close(); ss.close(); }
TCP通信程序的练习
package practise1.Demo5;
import java.io.*;
import java.net.Socket;
public class ServerThread implements Runnable {
private Socket s;
int count=0;
public ServerThread(Socket s) {
this.s=s;
}
@Override
public void run() {
//接收数据写道文本文件
try {
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
//BufferedWriter bw=new BufferedWriter(new FileWriter(file));
File file=new File("E:\\Java\\IP\\src\\practise1\\Demo5\\123["+count+"].java");
while(file.exists()){
count++;
file=new File("E:\\Java\\IP\\src\\practise1\\Demo5\\123["+count+"].java");
}
BufferedWriter bw=new BufferedWriter(new FileWriter(file));
String line;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//给出反馈
BufferedWriter bwServer=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
bwServer.write("文件上传成功");
bwServer.newLine();
bwServer.flush();
s.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package practise1.Demo5;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10098);
while(true){
//监听客户端连接
Socket s = ss.accept();
//为每一个客户端开启一个线程
new Thread(new ServerThread(s)).start();
}
}
}
package practise1.Demo5;
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket s=new Socket("192.168.137.1",10098);
String file="E:\\Java\\IP\\src\\practise1\\UDPReceiveDemo.java";
//封装文本文件数据
BufferedReader br=new BufferedReader(new FileReader(file));
//封装输出流写数据
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
s.shutdownOutput();
//接收反馈
BufferedReader brClient=new BufferedReader(new InputStreamReader(s.getInputStream()));
String data=brClient.readLine();
System.out.println("服务器的反馈:"+data);
//释放资源
br.close();
s.close();
}
}
连接:三次握手
取消连接:四次挥手
服务器端的优化
- 用循环优化
- UUID 不可变的通用唯一标识符的类
- 线程
- 线程池
package practise2.ThreadPool;
import java.io.*;
import java.net.Socket;
//将本地文件上传到服务器,接收服务器的反馈
public class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("172.18.45.241",10098);
//是本地的流,用来读取本地文件的
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("C:\\Users\\QQ363\\Desktop\\1.jpg"));
//写到服务器中要用网络的流
OutputStream os = socket.getOutputStream();
//包装提高效率
BufferedOutputStream bos=new BufferedOutputStream(os);
int b;
while ((b=bis.read())!=-1){
bos.write(b);//通过网络写到服务器中
}
//给服务器一个结束标识,文件已经传输
socket.shutdownOutput();
//接受反馈
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while((line=br.readLine())!=null){
System.out.println(line);
}
socket.close();
bis.close();
socket.close();
}
}
package practise2.ThreadPool;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
//接收客户端上传的问价你,上传完毕之后给出反馈
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss=new ServerSocket(10098);
//创建线程池
ThreadPoolExecutor pool=new ThreadPoolExecutor(
3, //核心线程数量
10,//线程池的总数量
60,//临时线程空闲时间
TimeUnit.SECONDS,//单位
new ArrayBlockingQueue<>(5),//阻塞队列
Executors.defaultThreadFactory(),//创建线程的方式
new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
);
while(true) {
Socket accept = ss.accept();
SocketThread st=new SocketThread(accept);
pool.submit(st);
}
}
}
package practise2.ThreadPool;
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class SocketThread implements Runnable{
private Socket accept;
public SocketThread(Socket accept) {
this.accept=accept;
}
@Override
public void run() {
BufferedOutputStream bos=null;
try {
//网络中的流,从客户端读取数据
BufferedInputStream bis=new BufferedInputStream(accept.getInputStream());
//本地的IO流,把数据写到本地中,实现永久化储存
bos=new BufferedOutputStream(new FileOutputStream("E:\\Java\\IP\\src\\practise2\\demo2\\"+ UUID.randomUUID().toString()+".jpg"));
int b;
while((b=bis.read())!=-1){
bos.write(b);
}
//给出反馈
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功!");
bw.newLine();
bw.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if (accept!=null){
try {
accept.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}