08 Java SE 网络编程
By Kevin Song
- 08-01 网络模型概述
- 08-02 TCP和UDP协议
- 08-03 网络编程其他
08-01 网络模型概述
网络模型
OSI(Open System Interconnection)开放系统互联参考模型
- 应用层
- 终端应用
- 表示层
- 把计算机能识别的东西表示成人能识别的东西
- 会话层
- 在系统之间发起会话,会话请求
- 传输层
- 定义传输协议和端口号
- TCP(传输控制协议)
- 传输效率低
- 可靠性强
- 传输数据量大的数据
- UDP(用户数据报协议)
- 可靠性低
- 传输数据量小的数据
- TCP(传输控制协议)
- 这一层数据叫:段
- 定义传输协议和端口号
- 网络层
- IP地址的封装和解封装
- 这一层数据叫:数据包
- 这一层设备是:路由器
- 数据链路层
- MAC地址的封装和解封装
- 这一层数据叫:帧
- 这一层设备是:交换机
- 物理层
- 定义物理设备标准
- 作用是传输比特流
- 这一层数据叫:比特
- 应用层
TCP/IP参考模型
- 应用层:数据段
- 传输层:数据包
- 网际互连层:数据帧
- 网络接入层:比特
网络通讯要素
- IP地址
- 网络中设备的标识
- 本地回环地址:127.0.0.1 主机名:localhost
- 端口号
- 用于标识进程的逻辑地址
- 有效端口:0 - 65535
- 系统保留端口:0 - 1024
- 传输协议
- 通讯的规则
- 常见协议
- TCP(传输控制协议)
- 建立链接,形成传输数据的通道
- 在连接中进行大数据量传输
- 通过三次握手完成连接,所以可靠
- Client:Server 帮个忙噻
- Server:干什么噻
- Client:那就谢谢了
- 因为要连接,所以速度慢
- 类似电话
- UDP(用户数据报协议)
- 数据和源目封装成数据包,不需要建立连接
- 每个数据报文限制在64k内
- 因为无连接,所以不可靠
- 因为无连接,所以速速快
- 类似对讲机
- TCP(传输控制协议)
InetAddress类
作用:InetAddress类表示互联网协议(IP)地址
public class IPDemo {
public static void main(String[] args) {
//获取本地主机ip地址对象
InetAddress ip = InetAddress.getLocalHost();
//获取其他主机ip地址对象
ip = InetAddress.getByName("192.168.1.100");
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
}
}
08-02 TCP和UDP协议
Socket
- Socket就是为网络服务提供的一种机制
- 通信的两段都有Socket
- 网络通信其实就是Socket间的通信
- 数据在两个Socket间通过IO传输
UDP协议
UDP协议发送端
创建UDP发送端思路
- 建立UDP的socket服务
- 将要发送的数据封装到数据包中
- 通过UDP的socket服务将数据包发送出去
- 关闭socket服务
public class UDPSendDemo {
public static void main(String[] args) {
//1.建立UDP的socket服务
DatagramSocket ds = new DatagramSocket();
//2.将要发送的数据封装到数据包中
String str = "UDP transmitting";
//用DatagramPacket将数据封装到该对象中
byte[] buf = str.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.100"),10000);
//3.通过UDP的Socket服务将数据包发送出去
ds.send(dp);
//4.关闭socket服务
ds.close();
}
}
UDP协议接收端
创建UDP接收端思路:
- 建立UDP的socket服务,必须明确端口号
- 将要接受的数据存储到数据包中
- 用socket的receive方法接收数据,存储到数据包中
- 通过数据包的方法解析数据包中的数据
- 关闭socket服务
public class UDPReceiveDemo {
public static void main(String[] args) {
//1.建立UDP的socket服务
DatagramSocket ds = new DatagramSocket(10000);
//2.创建数据包
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//3.使用接收方法将数据存储到数据包中
ds.receive(dp);
//4.通过数据包方法解析数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
//5.关闭资源
ds.close();
}
聊天程序
public class UDPSendDemo {
public static void main(String[] args) {
//1.建立UDP的socket服务
DatagramSocket ds = new DatagramSocket(8888);
//2.将要发送的数据封装到数据包中
//String str = "UDP transmitting";
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null) {
if("over".equals(line))
break;
byte[] buf = line.getBytes();
//用DatagramPacket将数据封装到该对象中
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.100"),10000);
//3.通过UDP的Socket服务将数据包发送出去
ds.send(dp);
}
//4.关闭socket服务
ds.close();
}
}
public class UDPReceiveDemo {
public static void main(String[] args) {
//1.建立UDP的socket服务
DatagramSocket ds = new DatagramSocket(10000);
while(true) { //持续接收数据
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//3.使用接收方法将数据存储到数据包中
ds.receive(dp);
//4.通过数据包方法解析数据
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+":"+port+":"+text);
}
//2.创建数据包
//5.关闭资源
//ds.close();
}
多线程实现的聊天程序
public class ChatDemo {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
DatagramSocket send = new DatagramSocket();
DatagramSocket rece = new DatagramSocket(10001);
new Thread(new Send(send)).start();
new Thread(new Rece(rece)).start();
}
}
public class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
this.ds = ds;
}
@Override
public void run() {
try {
while (true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();
String text = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip + "::" + text);
if(text.equals("886")){
System.out.println(ip+"");
}
}
} catch (Exception e) { }
}
}
public class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds = ds;
}
@Override
public void run() {
try {
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line=bufr.readLine())!=null){
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10001);
ds.send(dp);
if("886".equals(line))
break;
}
ds.close();
} catch (Exception e) {}
}
}
TCP协议
概述
- Socket和ServerSocket
- 建立客户端和服务端
- 建立连接后,通过Socket中的IO流进行数据的传输
- 关闭Socket
客户端
建立客户端思路
- 创建TCP客户端Socket服务
- 使用Socket对象
- 建议该对象已创建就明确目的地,要连接的主机
- 如果连接建立成功,说明数据传输通道已建立
- 通道就是Socket流,包含输入输出
- 通过Socket来获取输入输出对象
- 通过getOutputStream()和getInputStream()来获取输入输出字节流
- 使用输出流,发送到服务器
- 关闭资源
public class ClientDemo {
public static void main(String[] args) {
//1.创建客户端socket服务
Socket socket = new Socket("192.168.1.100", 10002);
//2.获取socket流中的输出流
OutputStream out = socket.getOutputStream();
//3.使用输出流将制定的数据写出去
out.write("TCP transmitting".getBytes());
//4.关闭资源
socket.close();
}
}
服务端
建立服务端思路
- 创建TCP服务端Socket服务
- 使用ServerSocket对象
- 服务端必须对外提供一个端口,否则客户端无法连接
- 获取连接过来的客户端对象
- 通过客户端对象获取socket流读取客户端发来的数据
- 关闭资源(客户端,服务端资源都要关闭)
public class ServerDemo {
public static void main(String[] args) {
//1.创建服务端socket服务
ServerSocket ss = new ServerSocket(10002);
//2.获取连接过来的客户端对象
Socket s = ss.accept();
String ip = s.getInetAddress().getHoustAddress();
//3.通过Socket对象读取输入流,读取客户端发来的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println("server:"+text);
//4.关闭客户端,关闭服务端
s.close();
ss.close();
}
}
客户端服务端交互
public class ClientDemo {
public static void main(String[] args) {
//1.创建客户端socket服务
Socket socket = new Socket("192.168.1.100", 10002);
//2.获取socket流中的输出流
OutputStream out = socket.getOutputStream();
//3.使用输出流将制定的数据写出去
out.write("TCP transmitting".getBytes());
//读取服务端返回的数据,使用socket读取流
InputStream in = socket.getInputsStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
//4.关闭资源
socket.close();
}
}
public class ServerDemo {
public static void main(String[] args) {
//1.创建服务端socket服务
ServerSocket ss = new ServerSocket(10002);
//2.获取连接过来的客户端对象
Socket s = ss.accept();
String ip = s.getInetAddress().getHoustAddress();
//3.通过Socket对象读取输入流,读取客户端发来的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(ip+":"+text);
//使用客户端Socket对象的输出流给客户端返回数据
OutputStream out = s.getOutpuStream();
out.write("Roger that".getBytes());
//4.关闭客户端,关闭服务端
s.close();
ss.close();
}
}
文本转换
客户端思路
- 创建socket客户端对象
- 获取键盘录入
- 将录入的信息发送给socket输出流
public class TransClient {
public static void main(String[] args) {
//1.创建socket客户端对象
Socket s = new Socket("192.168.1.100",10004);
//2.获取键盘输入
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//3.socket输出流
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//4.socket输入流,读取服务端返回的大写数据
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null) {
if("over".equals(line))
break;
out.println(line);
//读取服务器发回的一行大写数据
String upperStr = bufIn.readLine();
System.out.println(upperStr);
}
s.close();
}
}
服务端思路
- ServerSocket服务
- 获取Socket对象
- 源:socket,读取客户端发过来的需要转换的数据
- 目的:显示在控制台上
- 转换成大写发送给客户端
public class TransServer {
public static void main(String[] args) {
//1.ServerSocket服务
ServerSocket ss = new ServerSocket(10004);
//2.获取socket对象
Socket s = ss.accept();
//获取IP
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");
//3.获取socket读取流并装饰
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
//4.获取socket输出流并装饰
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line=bufIn.readLine())!=null){
System.out.println(line);
out.println(line.toUpperCase());
//out.print(line.toUpperCase()+"\r\n");
//out.flush();
}
s.close();
ss.close();
}
上传文本文件
public class UploadClient {
public static void main(String[] args) {
Socket s = new Socket("192.168.1.100",10005);
BufferedReader bufr = new BufferedReader(new FileReader("client.txt"));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line=bufr.readLine())!=null) {
out.println(line);
}
//告诉服务端,客户端写完了
s.shutdownOutput();
//out.println("over");
BufferedRedaer bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String str = bufIn.readLine();
System.out.println(str);
bufr.close();
s.close();
}
}
public class UploadServer {
public static void main(String[] args) {
ServerSocket ss = new ServerSocket(10005);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+"...connected");
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bufw = new BufferedWriter(new FileWriter("server.txt"));
String line = null;
while((line=bufIn.readLine())!=null) {
if("over".equals(line))
break
bufw.write(line);
bufw.newLine();
bufw.flush();
}
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("upload complete");
bufw.close();
s.close();
ss.close();
}
}
上传图片
public class UploadPicClient {
public static void main(String[] args) throws UnknownHostException, IOException {
//1,创建客户端socket。
Socket s = new Socket("192.168.1.100",10006);
//2,读取客户端要上传的图片文件。
FileInputStream fis = new FileInputStream("c:\\0.bmp");
//3,获取socket输出流,将读到图片数据发送给服务端。
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
out.write(buf,0,len);
}
//告诉服务端说:这边的数据发送完毕。让服务端停止读取。
s.shutdownOutput();
//读取服务端发回的内容。
InputStream in = s.getInputStream();
byte[] bufIn = new byte[1024];
int lenIn = in.read(buf);
String text = new String(buf,0,lenIn);
System.out.println(text);
fis.close();
s.close();
}
}
public class UploadPicServer {
public static void main(String[] args) throws IOException {
//创建tcp的socket服务端。
ServerSocket ss = new ServerSocket(10006);
//获取客户端
while(true) {
Socket s = ss.accept();
new Thread(new UploadTask(s)).start();
}
//ss.close();
}
}
public class UploadTask implements Runnable {
private Socket s;
private UploadTask(Socket s) {
this.s = s;
}
@Override
public void run() {
int count = 0;
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
//读取客户端发来的数据
try {
InputStream in = s.getInputStream();
//将读取到数据存储到一个文件中
File dir = new File("c:\\pic");
if(!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir,ip+".jpg");
if(file.exists()) {
file = new File(dir,ip+"("+(++count)+").jpg");
}
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf)!=-1)) {
fos.write(buf,0,len);
}
//获取socket输出流,把文件发送给客户端
OutputStream out = s.getOutputStream();
out.wirte("upload complete".getBytes());
fos.close();
s.close();
} catch (IOException e) {}
}
}
08-03 网络编程其他
常见客户端和服务端
常见的客户端
- 浏览器
- Chrome,Edge,Firefox
常见的服务端
- 服务器
- Tomcat
自定义客户端和服务端
自定义服务端
- 自定义服务端,使用已有的客户端IE,了解客户端给服务器端发了什么请求
public class MyTomcat {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(9090);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+".....connected");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
//给客户端一个反馈信息。
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("<font color='red' size='7'>欢迎光临</font>");
s.close();
ss.close();
}
}
服务端获取的信息如下:
请求行 请求方式
GET / HTTP/1.1 /myweb/1.html 请求的资源路径 http协议版本
请求消息头,属性名:属性值
**Accept: */*
Accept-Language: zh-cn, zu;q=0.5
Accept-Encoding:gzip, deflate
User-Agent: Mozilla/4.0
Host: 192.168.1.105:9090
Connection: Keep-Alive**
//空行(请求头和请求体之间有空行)
请求体
……
自定义客户端
public class MyBrowser {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket s = new Socket("192.168.1.100",8080);
//模拟浏览器,给tomcat服务端发送符合http协议的请求消息。
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("GET /myweb/1.html HTTP/1.1");
out.println("Accept: */*");
out.println("Host: 192.168.1.100:8080");
out.println("Connection: close");
out.println();
out.println();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String str =new String(buf,0,len);
System.out.println(str);
s.close();
//http://192.168.1.100:8080/myweb/1.html
}
}
浏览器获取的信息如下:
HTTP/1.1 200 OK //应答行:http的协议版本 应答状态码 应答状态描述信息
应答消息头 属性名:属性值
**Server: Apache-Coyote/1.1
Etag: W/”199-1323480176984”
Last-Modified: Sat, 10 Dec 2011 01:22:56 GMT
Content-Type:text/html
Content-Length: 100
Date: Fri, 11 May 2012 07:51:39 GMT
Connection: close**
//空行
应答体
<html>
<head>
<title></title>
</head>
<body>
<h1>欢迎光临</h1>
<font size="5" color="red"> html网页 </font>
</body>
</html>
应答状态码 | 应答状态描述信息 |
---|---|
200 | OK |
404 | not found |
URL&URL Connection
public class URLDemo {
public static void main(String[] args) throws MalformedURLException {
String str_url = "http://192.168.1.100:8080/myweb/1.html?name=lisi";
URL url = new URL(str_url);
/*
System.out.println(""+url.getProtocol());
System.out.println(""+url.getHost());
System.out.println(""+url.getPort());
System.out.println(""+url.getFile());
System.out.println(""+url.getPath());
System.out.println(""+url.getQuery());
*/
//InputStream in = url.openStream();
//获取url对象的url连接器对象,将连接封装成了对象
//Java中内置的可以解析的具体协议的对象+socket
URLConnection conn = url.openConnection();
//System.out.println(conn);
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
in.close();
}
}
常见网络架构
- C/S (Client/Server)
- 缺点
- 客户端服务端都需要开发
- 开发成本高,维护麻烦
- 优点
- 客户端在本地可以分担运算
- 缺点
- B/S (Browser/Server)
- 优点
- 服务端需要开发,客户端不需要开发,直接用浏览器
- 开发成本相对C/S低,维护简单
- 缺点
- 所有运算都在服务端完成
- 优点