网络编程是指在网络通信协议下,不同计算机上运行的程序,可以进行数据传输
IP地址:是网络中设备的唯一标识
IP地址分为两大类:
IPv4:是给每个连接在网络上的主机分配一个32bit地址。 例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”, 为了方便使用,IP地址经常被写成十进制的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。
IPv6:由于互联网的蓬勃发展,为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16位一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题。
特殊IP地址:127.0.0.1 是回送地址,可以代表本机地址,一般用来测试使用
InetAddress:此类表示Internet协议(IP)地址
相关方法:
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 |
String getHostName() | 获取此IP地址的主机名 |
String getHostAddress() | 返回文本显示中的IP地址字符串 |
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Demo01 {
public static void main(String[] args) throws UnknownHostException {
//获取本机InetAddress对象
InetAddress ia = InetAddress.getByName("172.20.0.15");
//得到本机IP地址的字符串形式
String hostAddress = ia.getHostAddress();
//得到本机的名称
String hostName = ia.getHostName();
System.out.println(hostAddress);
System.out.println(hostName);
}
}
端口:设备上应用程序的唯一标识
端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议:用户数据报协议(User Datagram Protocol)是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。
TCP协议:传输控制协议 (Transmission Control Protocol) 是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
Java中的UDP通信
构造方法:
方法名 | 说明 |
---|---|
DatagramSocket() | 创建数据报套接字并将其绑定到本机地址上的任何可用端口 |
DatagramPacket(byte[] buf,int len,InetAddress add,int port) | 创建数据包,发送长度为len的数据包到指定主机的指定端口 |
发送数据的步骤
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpSend {
public static void main(String[] args) {
DatagramSocket ds = null;
try {
//创建DatagramSocket对象,用于发送数据
ds = new DatagramSocket();
String str = "udp传输";
byte[] bytes = str.getBytes();
int len = bytes.length;
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
//将需要发送的数据使用DatagramPacket对象打包
DatagramPacket dp = new DatagramPacket(bytes, len, inetAddress, 10000);
//发送数据,参数为打包数据
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ds !=null){
//关闭发送端
ds.close();
}
}
}
}
接收数据的步骤
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UdpRecive {
public static void main(String[] args) {
DatagramSocket ds = null;
try {
//DatagramSocket对象用于接收数据,需要指定端口
ds = new DatagramSocket(10000);
byte[] bytes = new byte[1024];
int len = bytes.length;
//创建一个空包用于接收数据
DatagramPacket dp = new DatagramPacket(bytes, len);
//将包作为参数接收数据
ds.receive(dp);
//解析数据包
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data, 0, length));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (ds != null){
ds.close();
}
}
}
}
组播:
// 发送端
public class ClinetDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
String s = "hello 组播";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.1.0");
int port = 10000;
// 2. 创建数据,并把数据打包(DatagramPacket)
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 调用DatagramSocket对象的方法发送数据(在单播中,这里是发给指定IP的电脑但是在组播当中,这里是发给组播地址)
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端Socket对象(MulticastSocket)
MulticastSocket ms = new MulticastSocket(10000);
// 2. 创建一个箱子,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 把当前计算机绑定一个组播地址,表示添加到这一组中.
ms.joinGroup(InetAddress.getByName("224.0.1.0"));
// 4. 将数据接收到箱子中
ms.receive(dp);
// 5. 解析数据包,并打印数据
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 6. 释放资源
ms.close();
}
}
广播:
// 发送端
public class ClientDemo {
public static void main(String[] args) throws IOException {
// 1. 创建发送端Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket();
// 2. 创建存储数据的箱子,将广播地址封装进去
String s = "广播 hello";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("255.255.255.255");
int port = 10000;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
// 3. 发送数据
ds.send(dp);
// 4. 释放资源
ds.close();
}
}
// 接收端
public class ServerDemo {
public static void main(String[] args) throws IOException {
// 1. 创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds = new DatagramSocket(10000);
// 2. 创建一个数据包,用于接收数据
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
// 3. 调用DatagramSocket对象的方法接收数据
ds.receive(dp);
// 4. 解析数据包,并把数据在控制台显示
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
// 5. 关闭接收端
ds.close();
}
}
Java中的TCP通信
发送:
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象(Socket)
//Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
Socket s = new Socket("127.0.0.1",10000);
//获取输出流,写数据
//OutputStream getOutputStream() 返回此套接字的输出流
OutputStream os = s.getOutputStream();
os.write("hello,tcp,我来了".getBytes());
//释放资源
s.close();
}
}
接收:
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端的Socket对象(ServerSocket)
//ServerSocket(int port) 创建绑定到指定端口的服务器套接字
ServerSocket ss = new ServerSocket(10000);
//Socket accept() 侦听要连接到此套接字并接受它
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();
}
}
注意事项:
案例需求
客户端:数据来自于本地文件,接收服务器反馈
服务器:接收到的数据写入本地文件,给出反馈
客户端:
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
//bis为本地文件的字节缓冲输入流,用于读取需要上传的文件
FileInputStream fs = new FileInputStream("day11_exercise\\05.jpg");
BufferedInputStream bis = new BufferedInputStream(fs);
//os为网络中的输出流,向服务器传文件
OutputStream os = socket.getOutputStream();
//上传文件
int b;
while ((b = bis.read()) != -1) {
os.write(b);
}
socket.shutdownOutput();//关闭socket的输出流,并向流中写一个结束标志
//接收服务器的反馈为中文,需要使用转换流,传入socket获取的流和指定编码格式
InputStream socketInputStream = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(socketInputStream,"gbk");
BufferedReader br = new BufferedReader(isr);
String str;
while ((str = br.readLine())!=null){
System.out.print(str);
}
br.close();
socket.close();
bis.close();
}
}
服务器:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
//服务器将接收文件并保存到本地的功能使用多线程实现,可以同时存储多个客户端上传的文件
//这里使用的是重写Runnable中的run方法,也可以使用线程池减少开辟多个线程的开销
public class Service {
public static void main(String[] args) throws IOException {
//接收网络流的文件
ServerSocket ss = new ServerSocket(10086);
while (true) {
Socket accept = ss.accept();
SocketThread st = new SocketThread(accept);
Thread thread = new Thread(st);
thread.start();
}
// ss.close();
}
}
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;
BufferedWriter bw = null;
try {
//得到网络流,读入
InputStream is = accept.getInputStream();
//本地的输出流写到本地
FileOutputStream fs = new FileOutputStream("day15_exercise\\" + UUID.randomUUID().toString() + ".jpg");
bos = new BufferedOutputStream(fs);
//写到服务器本地
int b;
while ((b = is.read()) != -1) {
bos.write(b);
}
System.out.println("向客户反馈信息");
OutputStream socketos = accept.getOutputStream();
bw = new BufferedWriter(new OutputStreamWriter(socketos,"gbk"));
bw.write("上传成功!");
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如有错误欢迎留言评论,及时更正。2021年6月18日 羽露风