网络模型
OSI(Open System Interconnection 开放系统互连)参考模型
——(底层到上层)物理层,数据链路层,网络层,传输层,会话层,表示层,应用层
TCP(传输控制协议):传输效率低,可靠性强。用于传输的可靠性要求高,数据量大的数据。
UDP(用户数据报协议):与TCP特性相反,用于传输可靠性要求不高,数据量小的数据。
TCP/IP参考模型
——主机至网络,网络层,传输层,应用层
网络通讯要素
IP地址(InteAddress)
网络中设备的标识
不易记忆,可用主机名
本机回环地址:127.0.0.1 主机名:localhost
端口号
用于标识进程的逻辑地址,不同进程的标识
有效端口:0~65535,其中0~1024系统是使用或保留端口
传输协议
通讯的协议
常见协议:TCP、UDP
UDP
将数据及源和目的封装到数据包中,不需要建立连接;
每个数据包的大小限制在64K以内;
因无连接,是不可靠协议;
不需要建立连接,速度快。
TCP
建立连接,形成传输数据的通道;
在连接中进行大量数据传输;
通过三次握手完成连接,是可靠协议;
必须建立连接,效率稍低。
public class InetAddress
extends Object
implements Serializable
此类表示互联网协议 (IP) 地址。
static InetAddress getLocalHost() ;//返回本地主机。
InetAddress方法示例:
package cn.itcast.net.p1.ip; import java.net.InetAddress; import java.net.UnknownHostException; public class IPDemo { /** * @param args * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException { // 获取本地主机IP地址对象 InetAddress ip = InetAddress.getLocalHost(); // 获取其他主机的IP地址对象 // ip=InetAddress.getByName("192.168.0.101"); ip = InetAddress.getByName("www.baidu.com"); System.out.println(ip.getHostAddress()); System.out.println(ip.getHostName()); } }
Socket
Socket就是为网络服务提供的一种机制;
通信的两端都有Socket;
网络通信其实就是Socket间的通信;
数据在两个Socket间通过IO传输。
UDP传输
DatagramSocket和DatagramPacket
UDP传输示例:
package cn.itcast.net.p2.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPSendDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { System.out.println("发送端启动......"); /* * 创建UDP传输的发送端 * 思路: * 1.建立UDP的Socket服务; * 2.将要发送的数据封装到数据包中; * 3.通过UDP的Socket服务,将数据包发送出去; * 4.关闭Socket服务。 */ // 1.UDP的Scoket服务,使用DatagramSocket对象. DatagramSocket ds = new DatagramSocket(8888); // 2.将要发送的数据封装到数据包中。 String str = "UDP传输演示..."; // 使用DatagramPacket将对象封装到包中 byte[] buf = str.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.0.101"), 10000); //3.通过UDP的Socket服务将数据包发送出去。使用send方法 ds.send(dp); //4.关闭资源 ds.close(); } }
package cn.itcast.net.p2.udp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UDPReceiveDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { System.out.println("接收端启动......"); /* * 建立UDP接收端的思路: * 1.建立UDP的Socket服务;因为要接收数据,必须明确端口号; * 2.创建数据包,用于存储接收到的数据,方便用数据包对象的方法解析数据; * 3.使用Socket服务的receive方法将接收到的方法存储到数据包中; * 4.通过数据包的方法解析数据包中的数据; * 5.关闭资源。 */ //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); ds.close(); } }
基于多线程的聊天程序:
package cn.itcast.net.p3.chat; import java.io.IOException; import java.net.DatagramSocket; 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(); } } package cn.itcast.net.p3.chat; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * 发送端 * @author chenchong * */ 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.0.255"), 10001);//IP地址末尾为255,则发送广播 ds.send(dp); if ("886".equals(line)) break; } ds.close(); } catch (Exception e) { } } } package cn.itcast.net.p3.chat; import java.net.DatagramPacket; import java.net.DatagramSocket; /** * 接收端 * @author chenchong * */ 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 + ":" + port + ":" + text); if (text.equals("886")) { System.out.println(ip + ".....退出聊天室"); } // ds.close();//此处如果关闭资源,则只接收一次 } } catch (Exception e) { } ds.close(); } }
TCP传输
建立客户端和服务器端;
建立连接后,通过Socket中的IO流进行数据的传输;
关闭Socket。
客户端与服务器端是两个独立的应用程序。
public class Socket extends Object
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
public class ServerSocket extends Object
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果
TCP示例:
package cn.itcast.net.p4.tcp; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class ServerDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /* *服务端接收客户端发送过来的数据,并打印在控制台上 * * 建立TCP服务端: * 1.创建服务端Socket服务,通过ServerSocket对象; * 2.服务端必须对外提供一个端口,否则客户端无法连接; * 3.获取连接过来的客户端对象; * 4.通过客户端对象获取Socket流读取客户端发送的数据, * 并打印在控制台上; * 5.关闭资源:关客户端;关服务端。 */ //1.创建服务端对象 ServerSocket ss=new ServerSocket(10002); //2.获取连接过来的服务端对象 Socket s=ss.accept(); //3.通过Socket对象获取输入流,读取客户端发来的数据 InputStream in=s.getInputStream(); String ip=s.getInetAddress().getHostAddress(); //4.获取客户端发来的数据 byte[] buf=new byte[1024]; int len=in.read(buf); String text=new String(buf,0,len); System.out.println(ip+":"+text); //5.关闭资源 s.close(); ss.close(); } }
package cn.itcast.net.p4.tcp; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class ClientDemo { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { /* * 需求:客户端发送数据到服务端 * * TCP传输,客户端的建立 * 1.创建TCP客户端Socket服务,使用的是Socket对象, * 建议该对象一创建就明确目的地(要连接的主机); * 2.如果连接建立成功,说明数据传输通道已建立; * 通道就是Socket流,是底层建立的,这里既有输入又有输出。 * 想要输入或者输出流对象,可以找Socket流获取; * 可以通过getOutputStream()和getInputStream()来获取两个字节流; * 3.使用输出流,将数据写出; * 4.关闭资源。 */ // 创建客户端Socket服务 Socket socket = new Socket("192.168.0.101", 10002); // 获取Socket流中的输出流 OutputStream out = socket.getOutputStream(); // 使用输出流将指定的数据写出去 out.write("TCP演示:这里是客户端......".getBytes()); // 关闭资源(将连接断开) socket.close(); } }
服务端、客户端交互
package cn.itcast.net.p4.tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class ServerDemo2 { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { ServerSocket ss=new ServerSocket(10002); Socket s=ss.accept();//阻塞式 InputStream in=s.getInputStream(); String ip=s.getInetAddress().getHostAddress(); byte[] buf=new byte[1024]; int len=in.read(buf); String text=new String(buf,0,len); System.out.println(ip+":"+text); //使用客户端Socke对象的输出流给客户端返回数据 OutputStream out=s.getOutputStream(); out.write("server收到".getBytes()); //5.关闭资源 s.close(); ss.close(); } }
package cn.itcast.net.p4.tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class ClientDemo2 { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { Socket socket = new Socket("192.168.0.101", 10002); OutputStream out = socket.getOutputStream(); out.write("TCP演示:这里是客户端......".getBytes()); // 读取服务端返回的数据使用Socket读取流 InputStream in = socket.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); String text = new String(buf, 0, len); System.out.println(text); // 关闭资源(将连接断开) socket.close(); } }
文本转换客户端
package cn.itcast.net.p5.tcptest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class TransServer { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /* * 转换服务端: * 分析: * 1.ServerSocket服务; * 2.获取Socket对象; * 3.源:Socket,读取客户端发过来需要转换的数据; * 4.目的:显示在服务端控制台上; * 5.将数据转成大写发回客户端; * 6.关闭资源 */ // 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());//没有换行标记 // out.flush();//在不设置自动刷新时,需要调用flush()方法 } s.close(); ss.close(); } }
package cn.itcast.net.p5.tcptest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class TransClient { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { /* * 思路: * 客户端: * 1.需要Socket端点; * 2.客户端的数据源:键盘; * 3.客户端的目的:Socket; * 4.接收服务端的数据,源:socket; * 5.将数据打印出来,目的:控制台; * 6.在这些流中操作的数据,都是文本数据。 * * 转换客户端: * 1.创建Socket客户端对象; * 2.获取键盘录入; * 3.将录入的信息发送给Socket输出流; */ // 1.创建Socket对象 Socket socket = new Socket("192.168.0.101", 10004); // 2.获取键盘录入 BufferedReader bufr = new BufferedReader(new InputStreamReader( System.in)); // 3.目的是Socket输出流 // new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true);// 自动刷新 // 4.Socket输入流,读取服务端返回的大写数据 BufferedReader bufIn = new BufferedReader(new InputStreamReader( socket.getInputStream())); String line = null; while ((line = bufr.readLine()) != null) {// 读取到数据,如果没有读到换行标记,则认为没有读取完毕 if ("over".equals(line)) break; out.println(line);// 写入到PrintWriter中,需要刷新动作,才输出到Socket流中 // out.print(line);//没有换行标记 // out.flush();//在不设置自动刷新时,需要调用flush()方法 // 读取服务端发回的一行大写数据 String upperStr = bufIn.readLine(); System.out.println(upperStr); } socket.close();// 导致服务端结束 } }
如果不刷新,则数据暂存在PrintWriter中,不输出到Socket输出流,需要使用flush()方法;
如果readLine()没有读到换行标记,则任务数据没有读取完毕,继续等待;
上传文本文件
package cn.itcast.net.p6.uploadtext; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class UploadServer { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //1.创建ServerSocket服务端 ServerSocket ss=new ServerSocket(10005); //2.获取Socket对象 Socket s=ss.accept(); System.out.println(s.getInetAddress().getHostAddress()+"......connected."); //3.获取客户端读取流 BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream())); //4.创建目的文件对象 BufferedWriter bufw=new BufferedWriter(new FileWriter("c:\\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("上传成功"); bufw.close(); s.close(); ss.close(); } }
package cn.itcast.net.p6.uploadtext; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class UploadClient { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { // 1.创建Socket对象 Socket s = new Socket("192.168.0.101", 10005); // 2.创建源文件对象,源是文本文件 BufferedReader bufr = new BufferedReader(new FileReader( "c:\\client.txt")); // 3.目的是Socket输出流 PrintWriter out = new PrintWriter(s.getOutputStream(), true); String line = null; while ((line = bufr.readLine()) != null) { out.println(line); out.flush(); } // out.println("over"); // 告诉服务端,客户端数据已经写完了 s.shutdownOutput(); // 4.读取Socket流, BufferedReader bufIn = new BufferedReader(new InputStreamReader( s.getInputStream())); String str = bufIn.readLine(); System.out.println(str); bufr.close(); s.close(); } }
服务端多线程示例:
package cn.itcast.net.p1.uploadpic; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class UploadPicServer { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { // 1.创建TCP的Socket服务端 ServerSocket ss = new ServerSocket(10006); while (true) { // 2.获取客户端 Socket s = ss.accept(); new Thread(new UploadTask(s)).start(); } // ss.close(); } }
package cn.itcast.net.p1.uploadpic; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class UploadTask implements Runnable { private Socket s; public UploadTask(Socket s) { this.s = s; } @Override public void run() { int count=0; String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "......connected."); try { // 3.读取客户端发来的数据 InputStream in = s.getInputStream(); // 4.将读取到的数据存储到一个文件中 File dir = new File("c:\\pic"); if (!(dir.exists())) { dir.mkdirs(); } File file = new File(dir, ip + ".jpg"); //如果文件已经存在于服务器端 while(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); } // 给客户端发回:上传成功 OutputStream out = s.getOutputStream(); out.write("上传成功".getBytes()); fos.close(); s.close(); } catch (IOException e) { } } }
package cn.itcast.net.p1.uploadpic; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class UploadPicClient { /** * @param args * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { // 1.创建客户端 Socket s = new Socket("192.168.0.101", 10006); // 2.读取客户端要上传的图片文件 FileInputStream fis = new FileInputStream("c:\\0.jpg"); // 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(bufIn); String text = new String(bufIn, 0, lenIn); System.out.println(text); fis.close(); s.close(); } }
常见客户端和服务器
最常见的客户端:
浏览器:IE
最常见的服务端:
服务器:Tomcat
客户端和服务器端原理
1.自定义服务端,使用已有的客户端IE,了解客户端给服务器端发送了什么请求。
GET / HTTP/1.1 请求行:请求方式 /myweb/index.html 请求的资源路径 http协议版本
请求消息头,属性名:属性值
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding: gzip, deflate
Host: 192.168.0.101:9090
Connection: Keep-Alive
//请求头和请求体之间有一个空行,此行为空行
//请求体
模拟浏览器
//服务器发回的应答消息
HTTP/1.1 200 OK //应答行 http的协议版本,应答状态码,应答状态描述信息
//应答消息头。属性名:属性值
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"1807-1345210068813"
Last-Modified: Fri, 17 Aug 2012 13:27:48 GMT
Content-Type: text/html
Content-Length: 1807
Date: Fri, 17 Aug 2012 13:28:20 GMT
Connection: close
//空行
//应答体
<font color='red' size='7'>欢迎光临</font>
public final class URL extends Objectimplements Serializable
类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。
InputStream openStream()
打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。
打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。此方法是下面方法的缩写:
openConnection().getInputStream()
package cn.itcast.net.p2.ie_server; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; public class URLDemo { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { String str_url = "http://192.168.0.101:8080/myweb/2.html?name=lisi"; URL url = new URL(str_url); // System.out.println("getProtocol:"+url.getProtocol()); // System.out.println("getHost:"+url.getHost()); // System.out.println("getPath:"+url.getPath()); // System.out.println("getFile:"+url.getFile()); // System.out.println("getPath:"+url.getPath()); // System.out.println("getQuery:"+url.getQuery()); // 获取url对象的Url连接器对象,将连接封装成了对象:java中内置的可以解析具体协议的对象+socket URLConnection conn = url.openConnection(); String value=conn.getHeaderField("Content-Type");//text/html System.out.println(value); // System.out.println(conn); // 输出:sun.net.www.protocol.http.HttpURLConnection:http://192.168.0.101:8080/myweb/2.html?name=lisi // InputStream in=url.openStream();//打开到此 URL 的连接并返回一个用于从该连接读入的InputStream // 打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream。此方法是下面方法的缩写:openConnection().getInputStream() 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(); } }
网络架构
1.C/S Client/Server
缺点:
该结构的软件,客户端和服务端都需要编写;
开发成本较高,维护较为麻烦;
优点:
客户端在本地,可以分担一部分运算;
2.B/S Browser/Server
缺点:
所有运算都在服务器端完成;
优点:
该结构的软件,只开发服务器端,不开发客户端;因为客户端直接由浏览器取代;
开发成本相对较低,维护更为简单;