什么是网络编程
在网络通信协议下,实现网络互联的不同计算机上运行的程序可以进行资源交换,简而言之,用户之间可以发送消息或文件
网络编程三要素
IP地址
IP地址是网络中设备的唯一标识,分为IPv4和IPv6两大类,我们使用IPv4进行网络通信,它采用点分十进制表示法,一般长这样192.168.99.1
,127.0.0.1
可以代表本机地址,一般用于测试
命令 | 说明 |
---|---|
ipconfig | 查看本机IP |
ping IP | 检查网络是否连通 |
端口
网络的通信本质上是两个应用程序的通信,每台计算机上有许多程序,每个程序都运行在某一个端口上,端口号可以作为应用程序的唯一标识,普通应用程序要使用1024之后的端口号
协议
在计算机网络中,连接和通信的规则称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换,常见的协议有TCP协议和UDP协议
TCP
协议是面向连接
的通信协议,可以为两台计算机提供可靠无差别
的数据传输,在TCP协议中必须明确客户端与服务器,由客户端向服务器发起连接请求,每次连接要经过三次挥手
UDP
协议是面向无连接
的通信协议,,不管对方有没有收到,反正只负责发出去,由于UDP耗费资源小,通信效率高,通常会用于传输音频、视频、和普通数据以及直播
关于三次握手,四次挥手的理解
三次握手以及四次挥手其实就是确保客户端和服务器知道彼此
都具备接收和发送
数据的能力,以及做好了分开的准备,其中,以三次挥手为例。
- 客户端发起连接请求,服务器知道客户端有发送数据的能力,但是不知道客户端有没有接收的能力,与此同时,客户端不知道服务器是否具备接收和发送数据的能力
- 服务器回送响应,客户端知道服务器有了接受和发送数据的能力
- 客户端发送确认信息,服务器知道客户端接收到了上次的响应,服务器知道客户端有了接收数据的能力
InetAddress的使用
InetAddress是Java提供的一个类,方便我们对IP地址进行获取和操作
public class MyInetAddress {
public static void main(String[] args) throws UnknownHostException {
//通过主机名或ip地址获取对象
InetAddress address = InetAddress.getByName("192.168.99.1");
//获取主机名
String hostName = address.getHostName();
System.out.println("主机名:"+hostName);
//获取IP地址字符串
String hostAddress = address.getHostAddress();
System.out.println("IP地址:"+hostAddress);
}
}
UDP发送数据
Java提供了DatagramSocket类基于UDPP协议的Socket
发送数据的步骤:
- 创建Socket对象
- 创建数据并将数据打包
- 调用DatagramSocket对象的方法发送数据
- 关闭发送端
public class TcpSendDemo {
public static void main(String[] args) throws Exception {
String hostName = "192.168.99.1";
int port = 6666;
//创建socket对象
DatagramSocket socket = new DatagramSocket();
//准备数据,控制台读取
System.out.print("请输入你要发送的内容:");
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String text = reader.readLine();
byte[] data = text.getBytes();
//将数据打包
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(hostName),port);
//发送数据
socket.send(packet);
socket.close();
}
}
UDP接收数据
接收数据的步骤:
- 创建接收端的Socket对象
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
public class UdpReceiveDemo {
public static void main(String[] args) throws IOException {
//UDP接收只需要知道端口号就可以了
int port = 6666;
//创建socket对象
DatagramSocket socket = new DatagramSocket(port);
//创建一个数据包接收数据
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
//调用方法接收数据,阻塞等待
socket.receive(packet);
//解析数据包
byte[] data = packet.getData();
int length = packet.getLength();
//这里指定长度是消除后面的空字符,因为有时候不够1024
String dataString = new String(data, 0, length);
System.out.println("收到:"+dataString);
//关闭
socket.close();
}
}
TCP客户端发送数据
客户端和服务端会建立虚拟链路,是通过这个虚拟链路进行通信的。Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
发送数据的步骤:
- 创建客户端的Socket对象
- 获取输出流,写数据
- 释放资源
public class TcpClientDemo {
public static void main(String[] args) throws IOException {
String hostName = "192.168.99.1";
int port = 7777;
//创建客户端对象
Socket socket = new Socket(hostName, port);
//获取输出流,写数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("你好啊,tcp".getBytes());
//通知服务端,我传输完了
socket.shutdownOutput();
//得到服务器反馈
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
String dataString = new String(bytes, 0, len);
System.out.println("接收到服务器:"+dataString);
//释放资源
socket.close();
}
}
TCP服务器接收发送数据
接收数据的步骤:
- 创建服务器的Socket对象ServerSocket
- 获取输入流,读数据,把数据显示
- 释放资源
public class TcpServerDemo {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(7777);
//监听socket的连接
Socket accept = serverSocket.accept();
//获取输入流,读数据
InputStream inputStream = accept.getInputStream();
byte[] bytes = new byte[1024];
//一种是读字节,另一种是读字节数组,文本东西少,读一遍就好了
int len = inputStream.read(bytes);
String dataString = new String(bytes, 0, len);
System.out.println("接收客户端到:"+dataString);
//返回信息
OutputStream outputStream = accept.getOutputStream();
outputStream.write("已成功接收!".getBytes());
//关闭资源
serverSocket.close();
accept.close();
}
}
TCP中socket的补充
服务器可以通过accept方法获取道连接的socket,这个socket可以获取输入流(客户端传到服务器的信息),然后将输入流写入字符数组。同时这个accept得到的socket对象也可以获取输出流,用同样的套路去给客户端写东西,客户端的socket使用socket.getInputStream()
方法,使用同样的套路去获取服务器的信息
BufferedWriter的应用
在写的时候可以使用封装了OutputStream的BufferBuilder,直接调用write方法,将字符流转为字节流,功能等同于输出流的write。将getOutputStream
字节输出流转化为了OutputStreamWriter
字符输出流,再采用BufferedWriter
写数据
public class TcpClientDemo {
public static void main(String[] args) throws IOException {
String hostName = "192.168.99.1";
int port = 7777;
//创建客户端对象
Socket socket = new Socket(hostName, port);
//字符输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String text = reader.readLine();
//获取输出流,写数据
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line;
while ((line=reader.readLine())!=null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
//释放资源
socket.close();
}
}
BufferedReader的应用
将字节流直接转为字符流
public class TcpServerDemo {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(7777);
//监听socket的连接
Socket accept = serverSocket.accept();
//获取输入流,读数据
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
String line;
while ((line=bufferedReader.readLine())!=null) {
System.out.println("接收客户端到:"+line);
}
serverSocket.close();
accept.close();
}
}
TCP服务器内容写入文本文件
将FileWriter
封装为bufferedWriter
,客户端代码可以用上面的BufferedWriter的应用
的代码
public class TcpServerDemo {
public static void main(String[] args) throws IOException {
//创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(7777);
//监听socket的连接
Socket accept = serverSocket.accept();
//获取输入流,读数据
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
//把数据写入文本文件
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("a.txt"));
String line;
while ((line=bufferedReader.readLine())!=null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
bufferedReader.close();
bufferedWriter.close();
serverSocket.close();
accept.close();
}
}
TCP客户端内容来自文本文件
服务器的程序和上面TCP服务器内容写入文本文件
是一样的
public class TcpClientDemo {
public static void main(String[] args) throws IOException {
String hostName = "192.168.99.1";
int port = 7777;
//创建客户端对象
Socket socket = new Socket(hostName, port);
//字符输入流,读数据
BufferedReader bufferedReader = new BufferedReader(new FileReader("b.txt"));
//获取输出流,写数据
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line;
while ((line=bufferedReader.readLine())!=null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
//释放资源
bufferedReader.close();
bufferedWriter.close();
socket.close();
}
}
多线程实现文件上传
服务器一直开着,每来一个客户端就开一个线程,实现思路如下:
创建一个实现了Runnable
接口的类,构造方法中的参数是Socket类型,文件上传操作放在run方法中,服务器使用while死循环,accept得到的对象传入线程,然后调用start方法