08_网络编

来源:互联网 发布:进程调度算法c语言 编辑:程序博客网 时间:2024/06/04 23:22

一、网络编程概述

(1)、网络通讯三要素

       1、IP地址:

网络中设备的标识

IP有4位和6位。6位包含字母。

不宜记忆,可用主机名

本地默认IP:127.0.0.1主机名:localhost

       2、端口号:

数据要发送到对方指定的应用程序上,为了识别这些应用程序,所以给这些网络应用程序进行了标识,为了方便称呼这个数字,就叫做端口。(逻辑端口)

用于标识进程的逻辑地址,不同进程的标识。

有效端口:0-65535,其中,0-1024系统使用或者保留端口子

      3、传输协议:

定义通讯规则,这个通讯规则就称为协议。国际组织定义了通用协议TCP/IP。

常见协议:TCP,UDP

(2)、网络模型

OSI参考模型

TCP/IP参考模型

(3)、IP的使用

package cn.hwk;import java.net.*;public class IPDemo {public static void main(String[] args) throws Exception {try {// 本地主机ip地址对象// InetAddress i = InetAddress.getLocalHost();// 返回 IP 地址字符串(以文本表现形式)。// System.out.println("Address:" + i.getHostAddress());// 获取此 IP 地址的主机名。// System.out.println("Name:" + i.getHostName());// 获取此 IP 地址的主机名。// System.out.println(i.hashCode());// 获取其他主机ip地址对象InetAddress ia = InetAddress.getByName("www.taobao.com");System.out.println("Address:" + ia.getHostAddress());System.out.println("Name:" + ia.getHostName());} catch (UnknownHostException e) {e.printStackTrace();}}}

二、DDP、TCP、Socket

(1)、UDP:

将数据及源和目的封装成数据包中,不需要建立连接

每个数据包的大小限制在64k内

因为无连接,是不可靠协议

不需要建立连接,速度快

理解:就像到邮局中寄包裹一样,包要寄的东西打包,说明压迫寄往的地址和收件人(端口)

(2)、TCP:

建立连接,形成传输数据的通道

在连接中进行大数据传输

通过三次握手完成连接,是可靠协议

必须建立连接,效率会稍低

理解:打电话,需要先拨通对方号码(向对方发出通话请求),对方接听(表示对方收到请求),然后说话(告诉你他已收到请求)

(3)、Socket:

就是为网络服务提供一种机制

通信的两端都有Socket。//只要有了它,才能进行连接,连接后才有通路,现有码头,再有船

网络通信其实就是Socket间的通信

数据在两个Socket间通过IO传输

理解:有了码头,船才能在码头间来往。

三、UDP传输

(1)、关于UDP的传输,Java智能光提供了两个类:DatagramSocket和DatagramPacket

建立发送端、接收端。

调用Socket的发送接收方法。

关闭Socket。

(2)、DatagramSocket:此类表示用来发送和接收数据报包的套接字。

数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路

由,也可能按不同的顺序到达。

在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加

具体的地址时广播包也可以被接收。

示例:DatagramSocket s = new DatagramSocket(null); s.bind(new InetSocketAddress(8888)); 

这等价于:DatagramSocket s = new DatagramSocket(8888); 

两个例子都能创建能够在 UDP 8888 端口上接收广播的 DatagramSocket。

方法摘要:

receive(DatagramPacket p) 从此套接字接收数据报包。 

void send(DatagramPacket p) 从此套接字发送数据报包。 

(3)、DatagramPacket:此类表示数据报包。

数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,

也可能按不同的顺序到达。不对包投递做出保证。

构造方法摘要:

DatagramPacket(byte[] buf, int length) :构造 DatagramPacket,用来接收长度为 length 的数据包。 

DatagramPacket(byte[] buf, int length, InetAddress address, int port) :构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 

DatagramPacket(byte[] buf, int offset, int length) :构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。 

DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) :构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端

口号。 

DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) :构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口

号。 

DatagramPacket(byte[] buf, int length, SocketAddress address) :构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。

(4)、UDP建立发送端

package cn.hwk;//定义一个发送端import java.net.*;/*需求:通过udp传输方式,将一段文字数据发送出去 *      就是定义一个发送端。 * 思路: * 1.建立udpsocket服务 * 2.提供数据,并且将数据封装到数据包中 * 3.通过scoket服务的发送功能,将数据包发送出去 * 4.关闭资源 *  */public class UdpSend {public static void main(String[] args) throws Exception {// 1.建立udpsocket服务,通过DatagramSocket对象。建立发送端点DatagramSocket ds = new DatagramSocket(9999);// 2.提供数据,并且将数据封装到数据包中DatagramPacket(byte[] buf, int length, InetAddress// address, int port)byte[] buf = "upd你好啊".getBytes();DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("192.168.1.102"), 10000);// 3.通过scoket服务的发送功能,将数据包发送出去ds.send(dp);// 4.关闭资源ds.close();}}

(5)、UDP建立接收端

package cn.hwk;//定义一个udp的接收端import java.net.*;/* 需求:定义一个应用程序。用于接受udp协议传输的数据并处理 *          就是定义一个udp的接收端 *  * 思路: * 1.定义一个udpsocket服务.通常会监听一个端口。就是给这个接受网络应用程序定义数字标识。 *    方便于明确哪些数据过来,该应用程序可以处理。 *  * 2.定义一个数据包,因为要存储接受到的字节数据 *   因为数据包对象中有更多的功能可以提取字节数据中的不同数据信息 * 3.通过socket服务的receive方法,将收到的数据存入已定义好的数据包中 * 4.通过数据包对象的特有功能,将这些不同的数据取出,并打印在控制台上 * 5.关闭资源 */public class UpdReceive {public static void main(String[] args) throws Exception {// 1.创建udpcocket,建立接受端点DatagramSocket ds = new DatagramSocket(10000);while (true) {// 2.定义一个数据包,存储接受到的字节数据DatagramPacket(byte[] buf, int length)byte[] buf = new byte[1024 * 2];DatagramPacket dp = new DatagramPacket(buf, buf.length);// 3.通过socket服务的receive方法,将收到的数据存入已定义好的数据包中ds.receive(dp);// 阻塞式方法// 4.通过数据包对象的特有功能,将这些不哦她难过的数据取出,并打印在控制台上String ip = dp.getAddress().getHostAddress();String date = new String(dp.getData(), 0, dp.getLength());int port = dp.getPort();System.out.println(ip + "::" + date + "::" + port);}// 5,关闭资源// ds.close();}}

(6)、键盘录入在UDP通讯中的应用。

发送端:

package cn.hwk;import java.io.*;import java.net.*;public class UdpSend2 {public static void main(String[] args) throws Exception {DatagramSocket ds = new DatagramSocket();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 dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("192.168.1.102"), 10000);ds.send(dp);}ds.close();}}

接收端:

package cn.hwk;import java.net.*;import java.io.*;public class UdpReceive2 {public static void main(String[] args) throws Exception {DatagramSocket ds = new DatagramSocket(10000);while (true) {byte[] buf = new byte[1024];DatagramPacket dp = new DatagramPacket(buf, buf.length);ds.receive(dp);String ip = dp.getAddress().getHostAddress();String date = new String(dp.getData(), 0, dp.getLength());System.out.println(ip + ":::" + date);}}}

(7)、一个简单的聊天程序

package cn.hwk;/* 编写一个连天程序: *  * 有接收数据的部分,和发送数据的部分 * 这两个部分要同时执行 * 需要用到多线程技术 * 一个线程控制发送,一个线程控制接收 *  * 因为收和发的动作是不一致的,所以要定义两个run方法。 * 而且这两个方法要封装到不同的类中 */import java.io.*;import java.net.*;public class ChatDemo {public static void main(String[] args) {try {DatagramSocket sendsocket = new DatagramSocket();// 发送端DatagramSocket recesocket = new DatagramSocket(10001);// 接受端必须制定端口号Thread t1 = new Thread(new Send(sendsocket));// 发送端线程Thread t2 = new Thread(new Rece(recesocket));// 接收端 线程t1.start();t2.start();} catch (Exception e) {e.printStackTrace();}}}/* * 发送端类 */class Send implements Runnable {private DatagramSocket ds;// udpsocket服务public Send(DatagramSocket ds) {// 初始化一个发送端DatagramSocket对象this.ds = ds;}// 重写run方法,键盘录入io操作public void run() {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));// 键盘录入try {String line = null;while ((line = br.readLine()) != null) {// 循环读取if (line.equals("86"))// 自定义结束标记break;// 构造数据包,封装了数据,接受ip,接受端口,存储数据byte[] b = line.getBytes();DatagramPacket dp = new DatagramPacket(b, b.length,InetAddress.getByName("192.168.1.100"), 10001);// 封装数据包ds.send(dp);// 发送}} catch (Exception e) {try {throw new Exception("发送端失败");} catch (Exception e1) {e1.printStackTrace();}} finally {ds.close();// 关闭资源}}}/* * 接收发送端类 */class Rece implements Runnable {private DatagramSocket ds;public Rece(DatagramSocket ds) {// 初始化一个接受端DatagramSocket对象,接受数据 必须明确一个端口号this.ds = ds;}public void run() {// 重写线程run方法while (true) {byte[] b = new byte[1024];DatagramPacket dp = new DatagramPacket(b, b.length);// 创建数据包,用于存储接受的数据try {ds.receive(dp);// 阻塞式方法,将接受的数据存储到数据包中// 通过数据包获取ip,和转换后的字符串String ip = dp.getAddress().getHostName();// 通过数据包的方法解析包中的数据String data = new String(dp.getData(), 0, dp.getLength());System.out.println("ip" + ip + "data" + data);} catch (Exception e) {try {throw new Exception("发送端失败");} catch (Exception e1) {e1.printStackTrace();}} finally {ds.close();// 关闭资源}}}}

四、TCP传输

(1)、关于TCP的传输,Java中提供了两个类,Socket(客户端对象)和ServerSocket(服务端对象)

步骤:

建立客户端和服务端

建立连接后,通过Socket中的io流进行数据的传输

关闭Socket

同样,客户端与服务器端是两个独立的应用程序

客户端一建立,就要连接服务端

(2)、Socket(客户端对象):此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。

ServerSocket(服务端对象):此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。

(3)、TCP建立客服端

客户端:

通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机

因为tcp是面向连接的,所以在建立连接socket服务时。

就要有服务端存在,并连接成功。形成通路后,在该通道进行数据的传输。

package cn.hwk;import java.io.*;import java.net.*;/* *  * 需求:给服务端发送一个文本数据 *  * 步骤: * 1,创建TCP 客户端Socket服务,使用Socket对象。并指定要连接的主机和端口 *    建议该对象一创建就明确目的地。要连接主机 * 2,如果连接成功,说明数据的通道已建立 *    该通道是Socket流,是底层的,既然是流,说明这里既有输入又有输出 *    可以通过getOutputStream(),和getInputSteam()来获取两个字节流 * 3,使用输出流。将数据写出 * 4,关闭资源 */public class TcpClient {public static void main(String[] args) {try {// 创建客户端的socket服务,指定目的主机和端口Socket s = new Socket("192.168.1.102", 10001);// 为了发送数据,应该获取socket流中的输出流OutputStream out = s.getOutputStream();out.write("aaa".getBytes());s.close();// socket关闭,流也关闭了} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

(4)、TCP建立服务端

package cn.hwk;import java.io.*;import java.net.*;public class TcpServer {public static void main(String[] args) {Socket s = null;ServerSocket ss = null;try {// 建立服务端的socket,并监听一个端口ss = new ServerSocket(10001);// 通过accept方法获取连接过来的客户端对象s = ss.accept();// 阻塞式的方法String ip = s.getInetAddress().getHostAddress();System.out.println("ip:" + ip);// 获取客户端发送过来的数据,那么要使用客户端对象的读取流方法来读取数据InputStream in = s.getInputStream();byte[] buf = new byte[1024];System.out.println(new String(buf, 0, buf.length));} catch (IOException e) {e.printStackTrace();} finally {try {s.close();} catch (IOException e) {e.printStackTrace();}// 不关自己,关闭客户端try {ss.close();} catch (IOException e) {e.printStackTrace();}}}}

(5)、TCP建立交互方式

1、客户端

package cn.hwk;import java.io.*;import java.net.*;/* * 客服端: * 1,建立socket服务指定要连接主机和端口子 * 2,获取socket流中的输出流,将数据写到该流中,通过网络发送给服务端 */public class TcpClient2 {public static void main(String[] args) {try {Socket s = new Socket("192.168.1.102", 10002);OutputStream out = s.getOutputStream();out.write("服务端,你好".getBytes());InputStream in = s.getInputStream();byte[] buf = new byte[1024];int len = in.read();System.out.println(new String(buf, 0, len));s.close();} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

2、服务端

package cn.hwk;/* * 通过tcp服务的思路: * 1,建立服务端socket服务。通过ServerSocket对象  * 2,服务端必须对外提供一个借口,否则客户端无法连接 * 3,获取链接过来的客户端对象  * 4,通过客户端对象获取socket流读取客户端发来的数据 并打印在控制台上  * 5,关闭资源,管客户端,管服务端 */import java.io.*;import java.net.*;public class TcpServer2 {public static void main(String[] args) {// 服务端接受客户端发送过来的数据,并打印在控制台上try {// 1、创建服务端对象ServerSocket ss = new ServerSocket(10002);// 获取连接过来的客户端对象Socket s = ss.accept();// 2、获取ipString ip = s.getInetAddress().getHostAddress();System.out.println("ip:" + ip);// 3、通过socket对象获取输入流,要对客户端发过来的数据InputStream in = s.getInputStream();byte[] buf = new byte[1024];int len = in.read(buf);System.out.println(new String(buf, 0, len));// 4、使用客户端socket对象的输出流给客户端返回数据OutputStream out = s.getOutputStream();out.write("收到".getBytes());s.close();ss.close();} catch (IOException e) {e.printStackTrace();}}}

(6)、TCP复制文件

客户端:

package cn.hwk;import java.io.*;import java.net.*;public class TestClient {public static void main(String[] args) throws Exception {Socket s = new Socket("192.168.1.102", 10006);BufferedReader bufr = new BufferedReader(new FileReader("c:\\IPDemo.java"));PrintWriter out = new PrintWriter(s.getOutputStream(), true);String line = null;while ((line = bufr.readLine()) != null) {out.println(line);}out.println("over");BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));String str = bufIn.readLine();bufIn.close();s.close();}}

服务端:

package cn.hwk;import java.io.*;import java.net.*;public class TestServer {public static void main(String[] args) throws Exception {ServerSocket ss = new ServerSocket(10006);Socket s = ss.accept();BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));PrintWriter out = new PrintWriter(new FileWriter("c:\\server.txt"), true);String line = null;while ((line = bufIn.readLine()) != null) {if ("over".equals(line))break;out.println(line);}PrintWriter pw = new PrintWriter(s.getOutputStream(), true);pw.println("上传成功");out.close();s.close();ss.close();}}

(7)、TCP单张上传图片

客户端:

package cn.hwk;/*  * 单人上传图片  * 客户端。  * 1,服务端点  * 2,去读客户端已有的图片数据  * 3,通过socket输出流将数据发送给服务端  * 4,读取服务端反馈信息  * 5,关闭  */import java.net.*;import java.io.*;public class PicClient {public static void main(String[] args) throws Exception, IOException {Socket s = new Socket("192.168.1.102", 10008);FileInputStream fis = new FileInputStream("c:\\1.jpg");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 num = in.read(bufin);System.out.println(new String(bufin, 0, num));fis.close();s.close();}}

服务端:

package cn.hwk;import java.io.*;import java.net.*;/*服务端 */class PicServer {public static void main(String[] args) throws Exception {ServerSocket ss = new ServerSocket(10008);Socket s = ss.accept();InputStream in = s.getInputStream();FileOutputStream fos = new FileOutputStream("c:\\server.jpg");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();ss.close();}}

(8)、TCP客户端并发上传图片

这个服务端有个局限性。当a客户端连接上以后,被服务端获取到,服务端执行具体执行流程,

这时b客户端连接,只有等待

因为服务端还没有处理完A客户端的请求,还没有循环回来执行accept方法。所以暂时获取不到B客户对象。

那么为了可以让多个客户端同时并发访问服务

那么服务端最好就是将每个客户端封装到一个单独的线程中。这样,就可以同属处理多个客户端请求。

如何定义线程呢?

只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。

import java.io.*;import java.net.*;public class PicClient {public static void main(String[] args) throws Exception, IOException {// 主函数传值if (args.length != 1) {System.out.println("请选择一个jpd格式的图片");return;}File file = new File(args[0]);//根据传入的值,实例file对象if (!(file.exists() && file.isFile())) {//判断是否存在,是不是文件System.out.println("该文件有问题,要么不存在,要么不是文件");return;}if (!(file.getName().endsWith(".jpg"))) {//判断是不是以.jpg为后缀的System.out.println("图片格式错误,请重新选择");return;}if (file.length() > 1024 * 1024 * 5) {//对文件的大小进行设置System.out.println("文件过大");return;}Socket s = new Socket("192.168.1.100", 10004);//指定ip和端口号FileInputStream fis = new FileInputStream("c:\\1.jpg");//得到文件输入流,根据文件所在的路径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 num = in.read(bufin);//读取的字节数System.out.println(new String(bufin, 0, num));//打印输出fis.close();s.close();}}class PicServer {public static void main(String[] args) throws Exception {ServerSocket ss = new ServerSocket(10004);//服务端Socket指定端口while (true) {Socket s = ss.accept();//阻塞式接受new Thread(new PicThread(s)).start();//线程开启,多线程,并发上传}}}class PicThread implements Runnable {private Socket s;PicThread(Socket s) {this.s = s;}public void run() {int count = 1;// 如果定义到成员,多个线程共享数据了。不安全String ip = s.getInetAddress().getHostAddress();try {System.out.println(ip + "...connected");InputStream in = s.getInputStream();// 为了不覆盖,用ip地址来命名File file = new File(ip + "(" + (count) + ")" + ".jpg");while (file.exists()) {//判断文件是否存在file = new File(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 (Exception e) {throw new RuntimeException(ip + "上传失败");}}}

(9)、TCP客户端并发登录

客户端:

package cn.hwk;import java.io.*;import java.net.*;/* *客户端通过键盘录入用户名。 *服务端对这个用户名进行校验。 *如果该用户名存在,在服务端系那是xxx,已登录 *并在客户端显示xxx欢迎光临。 *如果该用户不存在在服务端显示xxx,尝试登陆。 *并在客户端显示xxx,该用户不存在。 *最多就登陆三次  */public class LoginClient {public static void main(String[] args) throws Exception, IOException {Socket s = new Socket("192.168.1.102", 10007);BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));PrintWriter out = new PrintWriter(s.getOutputStream(), true);BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));for (int x = 0; x < 3; x++) {String line = bufr.readLine();// 读一次if (line == null) {break;}out.println(line);// 发出去String info = bufin.readLine();// 读取服务端的反馈信息System.out.println("info:" + info);// 打印反馈信息if (info.contains("欢迎")) {break;}}bufr.close();s.close();}}

服务端:

package cn.hwk;import java.io.*;import java.net.*;public class LoinServer {public static void main(String[] args) throws Exception {ServerSocket ss = new ServerSocket(10007);while (true) {Socket s = ss.accept();new Thread(new UserThread(s)).start();}}}class UserThread implements Runnable {private Socket s;UserThread(Socket s) {this.s = s;}public void run() {String ip = s.getInetAddress().getHostAddress();System.out.println(ip + "...connected");try {for (int x = 0; x < 3; x++) {BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream()));String name = bufin.readLine();if (name == null) {// 客户端ctrl+c停止了break;}BufferedReader bufr = new BufferedReader(new FileReader("c:\\user.txt"));PrintWriter out = new PrintWriter(s.getOutputStream());String line = null;boolean flag = false;while ((line = bufr.readLine()) != null) {if (line.equals(name)) {flag = true;break;}}if (flag) {System.out.println(name + ",已登录");out.println(name + ",欢迎光临");break;} else {System.out.println(name + ",尝试登陆");out.println(name + ",用户名不存在");}}s.close();} catch (Exception ex) {throw new RuntimeException(ip + "校验失败");}}}
0 0