---------------------- android培训、java培训、期待与您交流! ----------------------
网络编程(概述)
网络模型
- OSI参考模型
- TCP/IP参考模型
网络通讯要素
- IP地址
- 端口号
- 传输协议
实现通讯的步骤
- 通过IP地址找到对方
- 通过指定的端口号连接到应用程序,实现数据传输。(为了将数据发送到指定的程序上,用来标识应用程序的的数字,为了方便称呼这些数字,命名它们为端口。这些端口是虚拟的,所以是逻辑端口。)
- 定义通讯规则,这个通讯规则称为协议。国际组织定义了通用协议Tcp/IP
网络模型
网络通讯要素IP地址:
- 网络中设备的标识
- 不易记忆,可用主机名
- 本地回环地址:127.0.0.1 主机名:localhost
端口号传输协议
- 用于标识进程的逻辑地址,不同进程的标识
- 有效端口:0-65535,其中0-1024系统使用或保留端口
- 通讯的规则
- 常见的协议:TCP,UDP
UDP
- 将数据源和目的封装成数据包中,不需要建立连接
- 每个数据报的大小限制在64k内
- 因为无连接,是不可靠协议
- 不需要建立连接,速度快
TCP
- 建立连接,形成传输数据的通道。
- 在连接中进行大数据量传输。
- 通过三次握手完成连接,是可靠协议。
- 必须建立连接。效率会稍低。
Socket
- Socket就是为网络服务提供的一种机制。
- 通信的两端都有Socket。
- 网络通信其实就是Socket间的通信。
- 数据在两个Socket间通过IO传输。
UDP发送
public class DatagramSocket
extends Object
此类表示用来发送和接收数据报包的套接字。
数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是单独编址和路由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。在 DatagramSocket 上总是启用 UDP 广播发送。为了接收广播包,应该将 DatagramSocket 绑定到通配符地址。在某些实现中,将 DatagramSocket 绑定到一个更加具体的地址时广播包也可以被接收。练习代码:/* * 需求:通过UDP传输方式,将一段文字数据发送出去。 * 思路: * 1,建立UdpSocket服务。 * 2,提供数据,并将数据封装到数据包中。 * 3,通过Socket服务的发送功能,将数据包发出去。 * 4,关闭资源。 */ public class DataGramSocketSendDemo { public static void main(String[] args) { //创建udp socket,建立端点 DatagramSocket ds = null; //定义数据包,用于接收数据。 byte[] value = "hello this is test data".getBytes(); try { DatagramPacket dap = new DatagramPacket(value,value.length,InetAddress.getByName("192.168.1.110"),6666); ds = new DatagramSocket(); ds.send(dap); } catch (Exception e) { throw new RuntimeException("udp数据发送异常"); }finally{ ds.close(); } } }
接收端:
/* * 需求: * 定义一应用程序,用于接收UDP协议传输的数据并处理。 * 定义UDP的接收端。 * 思路: * 1,定义UDPSocket服务,通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识。方便于明确哪些数据 * 来过该应用程序可以处理。 * 2,定义一个数据包,因为要存储接收到字节数据。 * 因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。 * 3,通过Socket服务的Receive方法将收到的数据存入已定义好的数据包中。 * 4,通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。 * 5,关闭资源。 */ public class DatagramSocketReceDemo { public static void main(String[] args) { DatagramSocket das = null; try { das = new DatagramSocket(6666); while(true){ byte[] value = new byte[1024]; DatagramPacket dap = new DatagramPacket(value,value.length); das.receive(dap); String str = new String(dap.getData(),0,dap.getLength()); int port = dap.getPort(); String ip = new String(dap.getAddress().getHostAddress()); System.out.println(str + "::" + ip + "::" + port); } } catch (Exception e) { // TODO: handle exception } } }
UDP聊天:public class UdpChat { public static void main(String[] args) { try { DatagramSocket send = new DatagramSocket(); DatagramSocket rece = new DatagramSocket(10002); new Thread(new SendClient(send)).start(); new Thread(new ReceClient(rece)).start(); } catch (Exception e) { throw new RuntimeException("接口创建失败"); } } } class ReceClient implements Runnable{ DatagramSocket das; public ReceClient(DatagramSocket das){ this.das = das; } @Override public void run() { try { byte[] value = new byte[1024]; DatagramPacket dap = new DatagramPacket(value, value.length); while(true){ das.receive(dap); String ip = dap.getAddress().getHostAddress(); String data = new String(dap.getData(),0,dap.getLength()); System.out.println(ip + "::" + data); } } catch (Exception e) { throw new RuntimeException("接收端异常"); } } } class SendClient implements Runnable{ DatagramSocket das; public SendClient(DatagramSocket das){ this.das = das; } @Override public void run() { try { byte[] value = new byte[1024]; BufferedReader buf = new BufferedReader(new InputStreamReader(System.in)); String len = null; while((len = buf.readLine()) != null){ if(len.equals("over")){ break; } value = len.getBytes(); //如果想群聊可以把ip地址换成广播地址,接收端地址使用同样的接收端口 DatagramPacket dap = new DatagramPacket(value, value.length,InetAddress.getByName("192.168.1.106"),10002); das.send(dap); } } catch (Exception e) { throw new RuntimeException("发送端异常"); } } }
TCP传输
- Socket和ServerSocket
- 建立客户端和服务器端
- 建立连接后,通过Socket中的IO流进行数据的传输
- 关闭Socket
- 同样,客户端与服务器端是两个独立的应用程序
练习代码:TCP服务端:/** * 需求:给服务端发送一个文本数据 * 步骤: * 1.创建Socket服务,并指定要连接的主机和端口 * @author onlin_000 * */ public class TcpSend { public static void main(String[] args) { try { //创建客户端的socket服务。指定目的主机和端口 Socket s = new Socket(InetAddress.getByName("192.168.1.106"),10003); //为了发送数据,应该获取socket流中的输出流。 OutputStream os = s.getOutputStream(); os.write("Hello this is test data!".getBytes()); s.close(); } catch (IOException e) { throw new RuntimeException("客户端异常"); } } }
/** * 需求:定义端点接收数据并打印在控制台上。 * 服务端: * 1.建立服务端的socket服务。ServerSocket(); 并监听一个端口 * 2.获取连接过来的客户端对象。通过ServerSocket的accept方法。没有连接就会等,所以这个方法是阻塞式的 * 3.客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流,来读取发过来的数据,并打印在控制台上。 * 4.关闭(可选) * @author onlin_000 * */ public class TcpServer { public static void main(String[] args) { ServerSocket ss = null; try { ss = new ServerSocket(10003); Socket s = ss.accept(); InputStream ins = s.getInputStream(); byte[] data = new byte[1024]; int len = ins.read(data); System.out.println(new String(data,0,len)); //关闭客户端连接 s.close(); } catch (Exception e) { throw new RuntimeException("服务端异常"); }finally{ if(ss != null){ try { ss.close(); } catch (IOException e1) { throw new RuntimeException("服务器关闭失败"); } } } } }
练习2:发送端:/** * 客户端通过键盘录入用户名。 * 服务端对这个用户名进行校验。 * * 如果该用户名存在,在服务器端显示xxx已登录。 * 并在客户端显示xxx欢迎光临。 * * 如果该用户不存在,正在服务器端显示xxx尝试登录。 * 并在客户端显示xxx用户不存在 * @author onlin_000 * */ public class TcpLoadServer { public static void main(String[] args) { ServerSocket ss = null; try { ss = new ServerSocket(10006); while(true){ Socket s = ss.accept(); new Thread(new LoadServer(s)).start(); } } catch (Exception e) { // TODO: handle exception } } } class LoadServer implements Runnable{ Socket s; LoadServer( Socket s){ this.s = s; } @Override public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + "..............connected"); BufferedReader bufin = null; try { bufin = new BufferedReader(new FileReader(new File("User.txt"))); BufferedReader buf = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter pw = new PrintWriter(s.getOutputStream(),true); for(int i = 0; i < 3; i++){ boolean flag = false; String name = buf.readLine(); if(name == null) return; String data = null; while((data = bufin.readLine()) != null){ if(name.equals(data)) flag = true; } if(flag){ System.out.println(name + "登录成功"); pw.println("欢迎" + name + "登录成功"); }else{ System.out.println(name + "尝试登录"); } } buf.close(); pw.close(); bufin.close(); } catch (Exception e) { throw new RuntimeException(ip + "登录失败"); } } }
Tcp练习3:public class TcpLoadClient { public static void main(String[] args) { BufferedReader bufin = null; try { bufin = new BufferedReader( new InputStreamReader(System.in)); Socket s = new Socket(InetAddress.getByName("192.168.1.111"),10006); BufferedReader buf = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter pw = new PrintWriter(s.getOutputStream(),true); String name = null; while((name = bufin.readLine())!= null){ if(name.equals("over")) return; pw.println(name); String re = buf.readLine(); if(re.contains("欢迎")) System.out.println(re); pw.close(); buf.close(); s.close(); } } catch (Exception e) { throw new RuntimeException("客户端异常"); } } }
客户端:/** * 让多个客户端用户能够同时并发上传文件 * 那么服务端最好就将每个客户端封装到一个单独的线程中,这样就可以同时处理对个客户端的请求。 * *如何定义线程呢? *只要明确了每一个客户端要在服务端执行的代码。 * @author onlin_000 * */ public class TcpUploadingByThreadServer { public static void main(String[] args) { while(true){ ServerSocket ss = null; try { ss = new ServerSocket(10006); Socket s = ss.accept(); new Thread(new clientByThread(s)).start(); } catch (IOException e) { throw new RuntimeException("服务器启动失败!"); }finally{ try { ss.close(); } catch (IOException e) { throw new RuntimeException("服务器关闭失败!"); } } } } } class clientByThread implements Runnable{ Socket s; clientByThread(Socket s){ this.s = s; } @Override public void run() { String ip = s.getInetAddress().getHostAddress(); int count = 0; try { File file = new File("server\\"+ ip + "(" + count + ")"+".bmp"); while(file.exists()){ file = new File("server\\"+ ip + "(" + (++count) + ")"+".bmp"); } FileOutputStream fos = new FileOutputStream(file); InputStream ins = s.getInputStream(); byte[] data = new byte[1024]; int len = 0; while((len = ins.read(data)) != -1){ fos.write(data, 0, len); } OutputStream outs = s.getOutputStream(); outs.write("上传成功!".getBytes()); fos.close(); ins.close(); outs.close(); s.close(); } catch (Exception e) { throw new RuntimeException(ip +"上传失败!"); } }
public class TcpUploadingByThreadClient { public static void main(String[] args) { try { Scanner sc = new Scanner(System.in); System.out.println("请输入图片路径,请用\\\\代替\\"); String path = sc.nextLine(); sc.close(); File file = new File(path); if(!file.exists()){ System.out.println("请输入正确的路径"); return; } if(!file.getName().endsWith(".bmp")||file.getName().endsWith(".BMP")){ System.out.println("请选择正确的文件"); return; } if(file.length()>1024*1024*5){ System.out.println("文件太大!"); return; } Socket s = new Socket(InetAddress.getByName("192.168.1.111"),10006); FileInputStream fis = new FileInputStream(file); OutputStream ous = s.getOutputStream(); byte[] buf =new byte[1024]; int len = 0; while((len = fis.read(buf)) != -1){ ous.write(buf, 0, len); } s.shutdownOutput(); InputStream ins = s.getInputStream(); int i =ins.read(buf); System.out.println(new String(buf,0,i)); ous.close(); fis.close(); s.close(); ins.close(); } catch (Exception e) { // TODO: handle exception } } }
练习3:TCP上传图片TCP客户端:public class TcpUploadingPiceServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10005); Socket s = ss.accept(); FileOutputStream fos = new FileOutputStream(new File("Server.bmp")); InputStream ins = s.getInputStream(); byte[] data = new byte[1024]; int len = 0; while((len = ins.read(data)) != -1){ fos.write(data, 0, len); } OutputStream outs = s.getOutputStream(); outs.write("上传成功!".getBytes()); fos.close(); ins.close(); outs.close(); s.close(); ss.close(); } }
TCP练习4:/** * 上传图片 * 客户端 * 1.服务端点 * 2.读取客户端已有的图片数据。 * 3.通过socket输出流将数据发给服务端。 * 4.读取服务端反馈信息。 * 5.关闭。 * @author onlin_000 * */ public class TcpUploadingPiceClient { public static void main(String[] args) throws Exception { Socket s = new Socket(InetAddress.getByName("192.168.1.111"),10005); FileInputStream fis = new FileInputStream(new File("test.bmp")); OutputStream ous = s.getOutputStream(); byte[] buf =new byte[1024]; int len = 0; while((len = fis.read(buf)) != -1){ ous.write(buf, 0, len); } s.shutdownOutput(); InputStream ins = s.getInputStream(); int i =ins.read(buf); System.out.println(new String(buf,0,i)); ous.close(); fis.close(); s.close(); ins.close(); } }
/** * 需求:建立一个文本转换服务器。 * 客户端给服务端发送文本,服务端会将文本转换成大写载返回给客户端。 * 而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束 * 分析: * 客户端:既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。 * 源:键盘录入。 * 目的:网络设备,网络输出流。 * 而且操作的是文本数据,可以选择字符流。 * 步骤: * 1.建立服务。 * 2.获取键盘录入。 * 3.将数据发给服务端。 * 4.获取服务端返回的大写数据。 * 5.结束,关闭资源。 * * 改例出现的问题。 * 现象:客户端和服务端都在莫名的等待。 * 原因:客户端和服务端都有阻塞式的方法。这些方法没有读到结束标记,那么就一直等待,而导致两端都在等待。 * 现象:第一次能正常运行,第二次报错 * 原因:使用过的端口没有被释放,第二次再次使用此端口会报错。 * @author onlin_000 * */ public class TransServer { public static void main(String[] args) { new Thread(new Server()).start(); new Thread(new Sender()).start(); } } class Sender implements Runnable{ @Override public void run() { Socket s = null; try { s = new Socket(InetAddress.getByName("192.168.1.106"),10008); //读取客户端输入的数据 BufferedReader bufin = new BufferedReader(new InputStreamReader(System.in)); //用于向流中写入数据 BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //用于读取服务器返回的大写数据 BufferedReader buf = new BufferedReader(new InputStreamReader(s.getInputStream())); String len = null; System.out.println("请输入"); while((len = bufin.readLine()) != null){ if(len.equals("over")){ break; } buw.write(len); buw.newLine(); buw.flush(); System.out.println(buf.readLine()); } } catch (Exception e) { throw new RuntimeException("发送端异常"); }finally{ if(s != null){ try { s.close(); } catch (IOException e) { throw new RuntimeException("客户端流关闭异常"); } } } } } class Server implements Runnable{ @Override public void run() { ServerSocket ss = null; try { ss = new ServerSocket(10008); Socket s = ss.accept(); //读取客户端发来的数据 BufferedReader bur = new BufferedReader(new InputStreamReader(s.getInputStream())); //写入处理后的数据 BufferedWriter buw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String len = null; while((len = bur.readLine()) != null){ buw.write(len.toUpperCase()); buw.newLine(); buw.flush(); } s.close(); ss.close(); } catch (Exception e) { throw new RuntimeException("服务端异常"); } } }
浏览器客户端-自定义服务端(severdemo.java)
public class ServerDemo { public static void main(String[] args) { ServerSocket ss; try { ss = new ServerSocket(10010); Socket s = ss.accept(); System.out.println(s.getInetAddress().getHostAddress() + "...............connected"); PrintWriter pw = new PrintWriter(s.getOutputStream(),true); pw.println("<font color = blue size = 5>Hello celient!</font>"); ss.close(); s.close(); } catch (IOException e) { throw new RuntimeException("服务端创建失败"); } } }
自定义浏览器-Tomcat服务器
---------------------- android培训、java培训、期待与您交流! ----------------------