Java网络编程
【网络编程】
网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据。
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。网络编程中
有两个主要的问题,一个是如何准确的定位网络上一台或多台主机,另一个就是找到主
机后如何可靠高效的进行数据传输。
网络编程技术是当前一种主流的编程技术,随着联网趋势的逐步增强以及网络应用程序
的大量出现,所以在实际的开发中网络编程技术获得了大量的使用。
【网络参考模型】
-
OSI参考模型
-
TCP/IP参考模型
【网络通讯要素】
-
IP地址:InetAddress
网络中设备的标识
不易记忆,可用主机名
本地回环地址:127.0.0.1 主机名:localhost
-
端口号
用于标识进程的逻辑地址,不同进程有不同的标识
有效的端口号:0~65535,其中0~1024为系统使用或保留端口,不建议使用
-
传输协议
通讯的规则
常见协议:TCP UDP
【IP地址】
Java.net包中InetAddress类此类表示互联网协议 (IP)地址。
该类未向外部提供构造方法,可以通过静态方法getLocalHost方法获取本类对象。
该类中的常用方法有:
public static InetAddress getLocalHost()throwsUnknownHostException:
返回本地主机。
public String getHostAddress():
返回 IP地址字符串(以文本表现形式)。
public String getHostName():
获取此 IP地址的主机名。
public static InetAddress getByName(String host)throwsUnknownHostException:
在给定主机名的情况下确定主机的 IP地址。
public static InetAddress[] getAllByName(String host)throwsUnknownHostException:
在给定主机名的情况下,根据系统上配置的名称服务返回其 IP地址所组成的
数组。
例:
// 获取本机IP地址类
InetAddress ia = InetAddress.getLocalHost();
// 获取本机IP地址
System.out.println("address:" + ia.getHostAddress());// 192.168.1.109
// 获取本机名
System.out.println("name:" + ia.getHostName());// FIRE
// 获取百度IP地址
InetAddress[] ias = InetAddress.getAllByName("www.baidu.com");
for (InetAddress i : ias) {
System.out.println("address:" + i.getHostAddress());
// 220.181.111.148
// 220.181.112.143
}
【TCP和UDP】
UDP:
不需要建立连接
将数据、源和目的封装成数据包,每个数据包的大小限制在64k内
由于是面向无连接,所以是不可靠协议
由于不需要建立连接,速度快
TCP:
需要建立连接,通过三次握手实现连接,形成传输数据通道
可以在连接中进行大数据量传输
由于面向连接,所以是可靠协议
由于需要建立连接,效率会稍低
【Socket】
Socket是为网络服务提供的一种机制,是两台机器间通讯的端点,通讯两端都要有
Socket。网络通讯其实就是Socket间的通讯,数据在两个Socket将通过IO传输
【UDP传输】
-
DatagramSocket与DatagramPacket
-
建立发送端,接收端
-
建立数据包
-
调用Socket的发送接收方法
-
关闭Socket
【DatagramSocket】
publicclass DatagramSocket extends Object:
此类表示用来发送和接收数据报包的套接字(端点)。数据报套接字是包投递服
务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路
由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按
不同的顺序到达。在 DatagramSocket上总是启用 UDP广播发送。为了接收
广播包,应该将 DatagramSocket绑定到通配符地址。在某些实现中,将
DatagramSocket绑定到一个更加具体的地址时广播包也可以被接收。
示例:
DatagramSocket s = new DatagramSocket(null);
s.bind(new InetSocketAddress(8888));
这等价于:
DatagramSocket s = new DatagramSocket(8888);
两个例子都能创建能够在 UDP 8888 端口上接收广播的 DatagramSocket。
publicvoid receive(DatagramPacket p)throwsIOException:从此套接字接收数据
报包,此方法在接收到数据报前一直阻塞
publicvoid send(DatagramPacket p)throws IOException:从此套接字发送数据报
包。
【DatagramPacket】
publicfinal class DatagramPacket extendsObject:
此类表示数据报包。数据报包用来实现无连接包投递服务。每条报文仅根据该
包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器
的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保
证。
构造方法:
publicDatagramPacket(byte[] buf,intlength)
public DatagramPacket(byte[]buf,int offset,int length)
构造数据包,用来接收长度为
length
的数据包。(offset为缓冲区中的偏移量)publicDatagramPacket(byte[] buf,intlength,InetAddress address,int port)
public DatagramPacket(byte[]buf,int offset,int length,InetAddress address,
int port)
构造数据报包,用来将长度为
length
的包发送到指定主机上的指定端口号。(offset为缓冲区中的偏移量)
DatagramPacket中的常用方法:
public byte[] getData():返回数据缓冲区。接收到的或将要发送的数据从
缓冲区中的偏移量offset处开始,持续 length 长度。
public int getLength():返回将要发送或接收到的数据的长度。
public int getPort():返回某台远程主机发送该数据包的端口号。
public InetAddress getAddress():返回某台机器发送该数据包的 IP地址
例:
import java.net.*;
import java.io.*;
class UdpSend2 {
public static void main(String[] args) {
DatagramSocket ds = null;
DatagramPacket dp = null;
BufferedReader bufr = null;
try {
// 创建端点,绑定12345端口
ds = new DatagramSocket(12345);
// 创建输入流对象,接收键盘录入
bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = bufr.readLine()) != null) {
// 停止接收键盘录入判断
if ("886".equals(line))
break;
byte[] buf = line.getBytes();
// 创建数据包,封装数据、目的IP和端口号
dp = new DatagramPacket(buf, buf.length, InetAddress
.getByName("FIRE"), 10000);
// 调用方法,发送数据包
ds.send(dp);
}
} catch (Exception e) {
System.out.println(e.toString());
} finally// 关闭资源
{
ds.close();
try {
if (bufr != null)
bufr.close();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
class UdpReceive2 {
public static void main(String[] args) {
DatagramSocket ds = null;
// 定义数组,用于存储数据
byte[] buf = new byte[1024];
DatagramPacket dp = null;
while (true)// 连续接收数据
{
try {
// 创建端点,绑定10000端口
ds = new DatagramSocket(10000);
// 创建数据包,用于接受数据
dp = new DatagramPacket(buf, buf.length);
// 调用方法,接收数据包
ds.receive(dp);
// 使用数据包中的方法,获取发送端IP地址,端口号,数据
String addr = dp.getAddress().getHostAddress();
int port = dp.getPort();
String data = new String(dp.getData(), 0, dp.getLength());
System.out.println(addr + "(" + port + ")" + ":");
System.out.println(data);
ds.close();
} catch (Exception e) {
System.out.println(e.toString());
}
}
}
}
【TCP传输】
-
Socket和ServerSocket
-
建立客户端和服务器端
-
建立连接后,通过Socket中的IO流进行数据的传输
-
关闭socket
客户端:
-
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
-
连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。
-
与服务端通讯结束后,关闭Socket。
通过Socket建立对象并指定要连接的服务端主机以及端口。
public class Socket extends Object:
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通
信的端点。
- public Socket():
创建一个未连接套接字。可以使用connect()方法连接到服务器
- public Socket(InetAddress address,int port)throws IOException:
创建一个流套接字并将其连接到指定 IP地址的指定端口号。
- public Socket(Stringhost,int port)
throwsUnknownHostException,IOException
创建一个流套接字并将其连接到指定主机上的指定端口号。
- public void connect(SocketAddressendpoint)throws IOException:
将此套接字连接到服务器。SocketAddress类中包含
IP 地址 +端口号
- public InputStream getInputStream()throwsIOException:
返回此套接字的输入流。
- public OutputStream getOutputStream()throwsIOException
返回此套接字的输入流。
服务器端:
-
服务端需要明确它要处理的数据是从哪个端口进入的。
-
当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。
-
当该客户端访问结束,关闭该客户端。
通过ServerSocket建立服务器端并监听一个端口
public class ServerSocket extends Object:
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该
请求执行某些操作,然后可能向请求者返回结果。
- public ServerSocket(int port)throws IOException:
创建绑定到特定端口的服务器套接字
- public ServerSocket(int port,int backlog)throws IOException:
利用指定的 backlog创建服务器套接字并将其绑定到指定的本地端
口号。backlog表示可同时接收客户端的最大数量
- public Socket accept()throws IOException:
侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
例:
import java.io.*;
import java.net.*;
/*
练习:TCP复制文件(将客户端的文件上传给服务器)
客户端:
发送数据:
源:Java源文件
目的:Socket输出流
纯文本,建议使用字符流
接收数据:
源:Socket输入流
目的:直接打印
建议使用字符流
*/
class TextClient {
public static void main(String[] args) {
// 创建客户端端点
Socket s = null;
try {
s = new Socket("FIRE", 11111);
} catch (Exception e) {
throw new RuntimeException("连接服务器失败!");
}
BufferedReader bufr = null;
try {
// 创建输入流,读取文件
bufr = new BufferedReader(new FileReader("IPDemo.java"));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s
.getOutputStream()));
// 使用打印流,将数据打印到Socket输出流中,true为自动刷新
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
// 创建输入流,读取Socket输入流中的数据,服务器发送的数据
BufferedReader in = new BufferedReader(new InputStreamReader(s
.getInputStream()));
// 读取文件中的数据,并将数据打印到Socket输出流中
String line = null;
while ((line = bufr.readLine()) != null) {
// out.write(line);
// out.newLine();
// out.flush();
pw.println(line);
}
// out.write("!!!");
// out.newLine();
// out.flush();
// pw.println("!!!");
// 关闭客户端的输出流,相当于给流中加了一个结束标记-1
s.shutdownOutput();
// 读取服务器发送的数据
String info = in.readLine();
System.out.println(info);
} catch (Exception e) {
throw new RuntimeException("上传失败!");
}
// 关闭资源
finally {
try {
if (bufr != null)
bufr.close();
} catch (IOException e) {
System.out.println("输入流关闭失败!");
}
try {
s.close();
} catch (IOException e) {
System.out.println("端点关闭失败!");
}
}
}
}
class TextServer {
public static void main(String[] args) {
// 创建服务器端点
ServerSocket ss = null;
try {
ss = new ServerSocket(11111);
} catch (Exception e) {
throw new RuntimeException("服务器端创建失败!");
}
// BufferedWriter bufw = null;
try {
// bufw = new BufferedWriter(new FileWriter("ul.txt"));
// 使用打印流,将数据打印到文件中,true为自动刷新
PrintWriter p = new PrintWriter(new FileWriter("ul.txt"), true);
// 获取客户端端点
Socket s = ss.accept();
// 创建输入流,读取Socket输入流中的数据,客户端发送的数据
BufferedReader in = new BufferedReader(new InputStreamReader(s
.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(s
.getOutputStream()));
// 使用打印流将数据打印到Socket输出流中,自动刷新
PrintWriter pw = new PrintWriter(s.getOutputStream(), true);
// 读取数据,并将数据打印到文件中
String line = null;
while ((line = in.readLine()) != null) {
// if("!!!".equals(line))
// break;
// bufw.write(line);
// bufw.newLine();
// bufw.flush();
p.println(line);
}
// out.write("");
// out.newLine();
// out.flush();
// 向Socket输出流打印数据
pw.println("上传成功!");
s.close();
} catch (Exception e) {
throw new RuntimeException("上传失败!");
}
// 关闭资源
finally {
// try
// {
// if(bufw != null)
// bufw.close();
// }
// catch(IOException e)
// {
// System.out.println("输出流关闭失败!");
// }
try {
ss.close();
} catch (IOException e) {
System.out.println("服务器端关闭失败!");
}
}
}
}