java基础(十二):网络编程
目录
基本概念
- 计算机网络
- 网络通信协议
- TCP协议和UDP协议
- IP地址和端口号
- URL统一资源定位符
- Socket套接字
TCP编程
实现用户登录功能
UDP编程
实现在线客服系统
一.计算机网络
计算机网络定义
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,
通过通信线路连接起来,
在网络操作系统,网络管理软件及网络通信协议的管理和协调下,
实现资源共享和信息传递的计算机系统
定义分析
主干:计算机网络是计算机系统
网络功能:资源共享 信息传递
网络组成:
网络硬件:计算机 外部设备 通信线路 (连接)
网络软件:网络操作系统 网络管理软件 网络通信协议 (管理和协调)
网络通信协议
计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准
网络通信协议的分层
名义上标准:ISO/OSI参考模型
事实上标准: TCP/IP协议栈(Internet使用的协议)
数据封装与拆封
1、封装(发送数据)
2、拆封(接收数据)
TCP/IP协议栈
网络层主要协议:IP协议
传输层主要协议:TCP和UDP
TCP协议和UDP协议
TCP(transfer control protocol)
是一种面向连接(连接导向)的、可靠的、基于字节流的运输层(Transport layer)通信协议 。
特点
- 面向连接
- 点到点的通信
- 高可靠性
- 占用系统资源多、效率低
生活案例
打电话
应用案例: HTTP FTP TELNET SMTP
UDP(User DatagramProtocol )
一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务
特点:
- 非面向连接,传输不可靠,可能丢失
- 发送不管对方是否准备好,接收方收到也不确认
- 可以广播发送
- 非常简单的协议,开销小
生活案例:
发送短信 发电报
应用案例: DNS SNMP
IP地址与端口
IP地址 :用来标志网络中的一个通信实体的地址。通信实体可以是计算机,路由器等。
IP地址分类
- IPV4:32位地址,以点分十进制表示,如192.168.0.1
- IPV6:128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
特殊的IP地址
127.0.0.1 本机地址
192.168.0.0--192.168.255.255私有地址,属于非注册地址,专门为组织机构内部使用。
端口:port
IP地址用来标志一台计算机,但是一台计算机上可能提供多种应用程序,使用端口来区分这些应用程序。
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。
端口范围0---65535,16位整数
端口分类
公认端口 0—1023 比如80端口分配给WWW,21端口分配给FTP 注册端口
1024—49151 分配给用户进程或应用程序
动态/私有端口 49152--65535
InetAddress类
封装计算机的ip地址 ,没有端口
//使用getLocalHost方法创建InetAddress对象 InetAddress addr = InetAddress.getLocalHost(); System.out.println(addr.getHostAddress()); //返回:192.168.1.110 System.out.println(addr.getHostName()); //输出计算机名 //根据域名得到InetAddress对象 addr = InetAddress.getByName(“www.163.com”); System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15 System.out.println(addr.getHostName()); //输出:www.163.com //根据ip得到InetAddress对象 addr = InetAddress.getByName(“61.135.253.15”); System.out.println(addr.getHostAddress()); //返回 163服务器的ip:61.135.253.15 System.out.println(addr.getHostName()); //输出ip而不是域名。如果这个IP地 址不存在或DNS服务器不允许进行IP地址和域名的映射,getHostName方法就直接返回这个IP地址。
InetSocketAddress
包含端口,用于socket通信的
//包含端口 InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080); InetSocketAddress socketAddress2 = new InetSocketAddress(“localhost”,9000); System.out.println(socketAddress.getHostName()); System.out.println(socketAddress2.getAddress());
URL(Uniform Resource Locator)
统一资源定位符,由4部分组成:协议 、存放资源的主机域名、端口号和资源文件名。
URL是指向互联网“资源”的指针
资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
URL u = new URL("http://www.baidu.con:80/index.html#aa?cansu=bjsxt"); System.out.println("获取与此url关联的协议的默认端口:"+u.getDefaultPort()); System.out.println(“getFile:”+u.getFile()); //端口号后面的内容 System.out.println("主机名:"+u.getHost()); //www.google.cn System.out.println(“路径:”+u.getPath()); //端口号后,参数前的内容 System.out.println(“端口:”+u.getPort()); //存在返回80.否则返回-1 System.out.println("协议:"+u.getProtocol()); System.out.println("参数部分:"+u.getQuery()); System.out.println("锚点:"+u.getRef()); URL u = new URL("http://www.abc.com/aa/"); URL u2 = new URL(u,“2.html”); //相对路径构建url对象 System.out.println(u2.toString()); //http://www.abc.com/aa/2.html
套接字Socket
我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协议,
在应用层如何使用传输层的服务呢?
在应用层和传输层之间,则是使用套接字来进行分离。
套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;
Socket实际是网络传输层供给应用层的编程接口。
传输层则在网络层的基础上提供进程到进程问的逻辑通道,
而应用层的进程则利用传输层向另一台主机的某一进程通信。
Socket就是应用层与传输层之间的桥梁。
使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
TCP编程
需求:
完成网络登录功能: 用户输入用户名密码,服务器给出登录成功或失败的提示
分析
- 使用基于TCP协议的Socket网络编程实现
- TCP协议基于请求-响应模式
- 在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序
- 第一次通讯中等待连接的程序被称作服务器端(Server)程序
- 利用IO流实现数据的传输
TCP通信原理(详细步骤)
- 服务器创建ServerSocket,在指定端口监听并并处理请求
- 客户端创建Socket,向服务器发送请求
网络登录功能分解
- 单向:客户端向服务器端发送字符串,服务器获取字符串并输出
- 双向:服务器端给出客户端反馈,客户端得到反馈并输出
- 对象:客户端向服务器端发送User对象,服务器端获取对象并输出
- 多线程:服务器接收多个客户端的请求,并给出反馈
- 每个客户请求开启一个线程
网络登录功能总结
- 服务器创建ServerSocket,在指定端口监听并并处理请求;客户端创建Socket,向服务器发送请求
- ServletSocket通过accept() 接收用户请求并返回对应的Socket,否则一种处于监听等待状态,线程也被阻塞
- 客户端发送数据需要输出流(写),客户端获取反馈数据需要输入流(读)
- 服务端反馈数据需要输出流(写),服务端获取请求数据需要输入流(读)
- 一旦使用ServerSocket和Socket建立了网络连接后,网络通信和普通IO流操作并没有太大区别
- 网络通信输出流建议使用DataOutputStream和ObjectOutputStream,与平台无关,输入流相应使用DataIntputStream和ObjectInputStream
- 如果是字符串通信也可以使用BufferedReader和PrintWriter,简单方便
最简单的服务器端代码
/** 最简单的服务器端代码*/ public class BasicSocketServer { public static void main(String[] args) { try { //建立服务器端套接字 ServerSocket serverSocket = new ServerSocket(8888); //tcp端口一共多少个?? //监听,等待客户端请求,并愿意接收连接。 System.out.println(“服务端建立监听”); Socket socket = serverSocket.accept(); //通过流向客户端发送数据 //ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); //oos.writeObject(“aaaaa”); //oos.close(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bw.write("hhhh"); bw.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
客户端
/** 最简单的Socket客户端 */ public class BasicSocketClient { public static void main(String[] args) { try { //指定的是所要连接的服务器的ip和端口。 //而不是自己机器的端口。发送端口是随机的。 Socket socket = new Socket(InetAddress.getLocalHost(),8888); //ObjectInputStream ois = new ObjectInputStream(socket.getInputStream()); //String string = (String) ois.readObject(); //System.out.println(string); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println(br.readLine()); br.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } }
客户端和服务器端可以双向交流的Socket程序
/**客户端和服务器端可以双向交流的Socket程序:*/ public class Server { public static void main(String[] args) throws Exception { ServerSocket server=new ServerSocket(8888); Socket socket=server.accept(); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); //用来读取客户端发送的消息的 BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //用来接收用户的输入消息,作为服务端的消息 while(true){ String str=in.readLine(); System.out.println("客户端说:"+str); String str2 = ""; str2 = br.readLine(); //读到\n为止,因此一定要输入换行符! out.write(str2+"\n"); out.flush(); if(str.equals("end")) break; } in.close(); out.close(); socket.close(); } }
客户端
/**客户端和服务器端可以双向交流的Socket程序: */ public class Client { static Socket server; public static void main(String[] args) throws Exception { server=new Socket(InetAddress.getLocalHost(),8888); BufferedReader in=new BufferedReader(new InputStreamReader(server.getInputStream())); BufferedWriter out=new BufferedWriter(new OutputStreamWriter(server.getOutputStream())); BufferedReader wt=new BufferedReader(new InputStreamReader(System.in)); while(true){ String str=wt.readLine(); out.write(str+“\r\n" ); //一定要加。不加的话,那边按照readLine读的话,如果没读到\n就会一直阻塞! out.flush(); if(str.equals("end")) break; System.out.println("服务器发说:"+in.readLine()); } out.close(); in.close(); wt.close(); server.close(); } }
UDP编程
需求:
完成在线咨询功能: 客户和咨询师在线交流
分析
- 使用基于UDP协议的Socket网络编程实现
- 不需要利用IO流实现数据的传输
- 每个数据发送单元被统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中去寻找他的目的地。
UDP基本概念
- DatagramSocket:用于发送或接收数据包
- DatagramPacket:数据包
udp实现
客户端
/**客户端向服务器端发送信息(最基本的操作) */ public class UDPClient { public static void main(String[] args) throws Exception { byte[] b = "aaaa".getBytes(); //必须告诉数据包要发到哪里去 DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost",8999)); //我本身占用9000端口向外面机器发数据包 DatagramSocket ds = new DatagramSocket(9000); ds.send(dp); ds.close(); } }
服务端
/**客户端向服务器端发送信息(最基本的操作) */ public class UDPServer { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999); byte[] b = new byte[1024]; DatagramPacket dp = new DatagramPacket(b,b.length); ds.receive(dp); //阻塞式方法 String string = new String(dp.getData(),0,dp.getLength()); //dp.getLength()返回实际收到的数据的字节数 System.out.println(string); ds.close(); } }
实现2
/**客户端向服务器端发送信息(传递基本类型数据) */ public class Client { public static void main(String[] args) throws Exception { long n = 2000L; ByteArrayOutputStream bos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(bos); dos.writeLong(n); byte[] b = bos.toByteArray(); //必须告诉数据包要发到哪里去 DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost",8999)); //我本身占用9000端口向外面机器发数据包 DatagramSocket ds = new DatagramSocket(9000); ds.send(dp); ds.close(); } }
/**客户端向服务器端发送信息(传递基本类型数据) */
public class Server { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999); byte[] b = new byte[1024]; DatagramPacket dp = new DatagramPacket(b,b.length); ds.receive(dp); //阻塞式方法 ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData()); DataInputStream dis = new DataInputStream(bis);
System.out.println(dis.readLong()); ds.close(); } }
实现3
/**传递对象 */ class Person implements Serializable{ int age; String name; }
/**客户端向服务器端发送信息(传递对象) */ public class Client { public static void main(String[] args) throws Exception { Person person = new Person(20,"aa"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(person); byte[] b = bos.toByteArray(); //必须告诉数据包要发到哪里去 DatagramPacket dp = new DatagramPacket(b,b.length,new InetSocketAddress("localhost",8999)); //我本身占用9000端口向外面机器发数据包 DatagramSocket ds = new DatagramSocket(9000); ds.send(dp); ds.close(); } }
/**客户端向服务器端发送信息(传递对象) */ public class Server { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(8999); byte[] b = new byte[1024]; DatagramPacket dp = new DatagramPacket(b,b.length); ds.receive(dp); //阻塞式方法 ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData()); ObjectInputStream ois = new ObjectInputStream(bis); Person person = (Person) ois.readObject();
System.out.println(person.name); ds.close(); } }