java-网络编程
1 网络模型
2 网络通讯要素
IP
网络中的标识
本地回环地址:127.0.01 主机名 localhost
端口
用于标识进程的逻辑地址,不同的进程的标识
有效的端口0-65535 0-1024 系统使用或保留端口
传输协议
通讯的规则
常见协议:TCP UDP
UDP:对讲机
TCP:打电话
3 域名解析
4 UDP
1,发送时,创建待了送数据包pack,其中指定目标主机的ip地址和接收的端口
调用DatagemSocket 的send(pack)方法发送pack数据报,发送端口由java绑定一个可用的端口
2,接收时,创建指定的端口的DatagramScoket 调用基receve(pack)方法招收数据报接收的数据存放在pack包的缓冲区中,在调用pack.getData()方法获得里面的数据
接收方必须指定接收端口,该接收端口正是发送数据包中指定的端口,而发送端口可以是任意一个可用的端口
发送
/* * 思路: * 1,建立udp的socket服务, * 2,将要发送的数据封装支数据包中去 * 3,通过udp的socket服务将数据包发送出去 * 4,关闭socket服务 * * */ System.out.println("发送端启动"); DatagramSocket ds = new DatagramSocket(); String str= "我来了"; byte [] buf = str.getBytes(); DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("127.0.0.1"), 10000); ds.send(dp); ds.close();
接收
/* * 接收端 * 思路: * 1,建立scoket服务,因为要接收数据,必须要明确端口号 * 2,创建数据包,用于存储接收到的数据,方便解析 * 3.使用scoket服务的receive方法接收的数据存储到数据包中 * 4,通过数据包的方法解析数据包的数据 * 5.关闭资源 * * */ System.out.println("接收启动"); // 建立 udp scoket服务 DatagramSocket ds = new DatagramSocket(10000); // 创建数据包 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); ds.close();
5 聊天程序
发送端
DatagramSocket ds = new DatagramSocket(); // 加上键盘录入 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("127.0.0.1"), 10001); ds.send(dp); if("over".equals(line)) break; }
接收端
Whlie循环,让它一直接收
上面是两个进程的工作,还要切换进程 ,可以创建两个线程,让它在五一个 进程中.这时要用到线程技术
发送端
class Test implements Runnable { private DatagramSocket ds; public Test (DatagramSocket ds){ this.ds=ds; } @Override public void run() { try{ System.out.println("发送端启动"); // 加上键盘录入 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("127.0.0.1"), 10002); ds.send(dp); if("over".equals(line)) break; } ds.close(); }catch(Exception e){ } } }
接收端
public class Test2 implements Runnable { private DatagramSocket ds ; public Test2(DatagramSocket ds){ this.ds=ds; } @Override public void run() { try{ while(true){ System.out.println("接收启动"); // 建立 udp scoket服务 // 创建数据包 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); } }catch(Exception e){ } } }
数据测试
public static void main(String[] args) throws SocketException { DatagramSocket send = new DatagramSocket(); DatagramSocket rec = new DatagramSocket(10002); Test s = new Test (send); Test2 r = new Test2(rec); new Thread(s).start(); new Thread(r).start();
6 TCP
客户端
// tcp传输.客户端建立过程 /* * * 1,创建tcp 客户端socket服务,使用的是scoket对象 * 2.如果连接建立成功,说明数据通道连接成功 * 可以通过getOutputSteam() 和getInputStream来获取两个字节流 * 3, 使用输出流 或者输入流 * * */ // 创建客户端服务 Socket socket = new Socket("127.0.0.1",20000); //获取流 OutputStream out = socket.getOutputStream(); // 使用输出流将指定的数据写出去 out.write("人的家".getBytes()); // 关闭资源 socket.close();
服务端
public static void main(String[] args) throws IOException { /* * 建立tcp服务端折思路 * 1,创建服务端socket服务,通过serverSocket对象 * 2,服务端必须提供一个端口,否则客户端无法连接 * 3, 获取连接过来的客户端对象 * 4,通过客户端对象获取 socket流读取客户端发来的数据 * 5.关闭资源,关客房端,关服务端 * * */ ServerSocket ss = new ServerSocket(20000); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); 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); System.out.println(ip); s.close(); ss.close(); }
7 文本转换
易错点:
1,结束标记的原理
2,当遇到上传过程中,会发生都在阻塞状态,
文本转换-客户端
public class TransClient { /** * @param args * @throws IOException * @throws */ public static void main(String[] args) throws IOException { System.out.println("客户端运行......"); // 客户端: // 步骤: // 1,创建socket,明确地址和端口。 Socket s = new Socket("192.168.1.223", 10005); // 2,源:键盘录入。获取需要转换的数据。 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); // 3,目的:网络,socket输出流。将录入的数据发送到服务端。 // OutputStream out = s.getOutputStream(); //既然都是字符数据,为了便于操作,使用额外功能,转换。同时提高效率。 //BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(out));可以使用打印流 PrintWriter out = new PrintWriter(s.getOutputStream(),true); // 4,源:socket,socket读取流,读取服务端发回来的大写数据。 // InputStream in = s.getInputStream(); BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream())); // 5,目的:客户端显示器,将大写数据显示出来。直接使用输出语句。 // 6,频繁的读写操作。 String line = null; while((line=bufr.readLine())!=null){ //将读取键盘的数据发送到服务端。 out.println(line); // out.flush(); if("over".equals(line)){ break; } //读取服务端返回的数据。 String upperText = bufIn.readLine(); System.out.println(upperText); } // 7,关闭资源。 s.close(); } }
文本转换-服务端
public class TransServer { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { System.out.println("服务端运行...."); // 服务端: // 思路: // 1,创建服务端socket 明确端口。 ServerSocket ss = new ServerSocket(10005); while (true) { // 获取客户端对象。 Socket s = ss.accept(); System.out.println(s.getInetAddress().getHostAddress()+".....connected"); // 2,源:socket输入流。读取客户端的发过来的数据。 BufferedReader bufIn = new BufferedReader(new InputStreamReader( s.getInputStream())); // 3,目的:socket输出流。将转成大写的数据发送给客户端。 PrintWriter out = new PrintWriter(s.getOutputStream(),true); // 4,频繁的读写操作。 String line = null; while ((line = bufIn.readLine()) != null) { if("over".equals(line)){//如果客户端发过来的是over,转换结束。 break; } System.out.println(line); // 将读取到的字母数据转成大写,发回给客户端。 out.println(line.toUpperCase()); // out.flush(); } // 5,关闭客户端。 s.close(); } } }
8 上传文件
易错点:
1结束标记的原理
客户端
public static void main(String[] args) throws UnknownHostException, IOException { System.out.println("上传文件客户端运行......"); // 客户端: // 步骤: // 1,创建socket,明确地址和端口。 Socket s = new Socket("192.168.1.223", 10006); // 2,源:读取文本文件。获取需要转换的数据。 BufferedReader bufr = new BufferedReader(new FileReader("tempfile\\client.txt")); // 3,目的:网络,socket输出流。将录入的数据发送到服务端。 PrintWriter out = new PrintWriter(s.getOutputStream(),true); // 4,频繁的读写操作。 String line = null; while((line=bufr.readLine())!=null){ out.println(line); } //给服务端发送一个结束标记。这个标记是约定标记。有点麻烦。可以更简单。使用socket对象的shutdownOutput(); // out.println("over"); s.shutdownOutput();//向服务端发送了结束标记。可以让服务端结束读取的动作。 // 5,源:socket,socket读取流,读取服务端发回来的上传成功信息。 BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream())); String info = bufIn.readLine(); System.out.println(info); // 6,关闭资源。 bufr.close(); s.close(); }
服务器端
public static void main(String[] args) throws IOException { System.out.println("上传文本服务端运行...."); // 服务端: // 思路: // 1,创建服务端socket 明确端口。 ServerSocket ss = new ServerSocket(10006); while (true) { // 获取客户端对象。 Socket s = ss.accept(); System.out.println(s.getInetAddress().getHostAddress()+".....connected"); // 2,源:socket输入流。读取客户端的发过来的数据。 BufferedReader bufIn = new BufferedReader(new InputStreamReader( s.getInputStream())); // 3,目的:文件。 PrintWriter pw = new PrintWriter(new FileWriter("tempfile\\server.txt"),true); // 4,频繁的读写操作。 String line = null; while ((line = bufIn.readLine()) != null) { // if("over".equals(line)){ // break; // } pw.println(line); } // 5,发回给客户端上传成功字样。 PrintWriter out = new PrintWriter(s.getOutputStream(),true); out.println("上传成功"); // 6,关闭客户端。 s.close(); } }
9 字符流不可以传输媒体文件
有可能你在查表时不能查到,造成你你原来文件和转换后的不一样.
比如下面图片里面的”1122”,查不到,那么表会给他一个默认的编码”1122”
10 上传图片
客户端
上面图片要用到字节流 public static void main(String[] args) throws IOException { System.out.println("上传图片客户端运行......"); //1,创建socket。 Socket s = new Socket("192.168.1.223", 10007); //2,读取源图片。 File picFile = new File("tempfile\\1.jpg"); FileInputStream fis = new FileInputStream(picFile); //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); System.out.println(new String(bufIn,0,lenIn)); //关闭。 fis.close(); s.close(); }
服务端
String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + ".....connected"); // 读取图片数据。 InputStream in = s.getInputStream(); // 写图片数据到文件。 File dir = new File("e:\\uploadpic"); if (!dir.exists()) { dir.mkdir(); } // 为了避免覆盖,通过给重名的文件进行编号。 int count = 1; File picFile = new File(dir, ip + "(" + count + ").jpg"); while (picFile.exists()) { count++; picFile = new File(dir, ip + "(" + count + ").jpg"); } FileOutputStream fos = new FileOutputStream(picFile); 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();
解决重名覆盖问题
// 为了避免覆盖,通过给重名的文件进行编号。 int count = 1; File picFile = new File(dir, ip + "(" + count + ").jpg"); while (picFile.exists()) { count++; picFile = new File(dir, ip + "(" + count + ").jpg"); }
解决并发问题
public void run() { try { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip + ".....connected"); // 读取图片数据。 InputStream in = s.getInputStream(); // 写图片数据到文件。 File dir = new File("e:\\uploadpic"); if (!dir.exists()) { dir.mkdir(); } // 为了避免覆盖,通过给重名的文件进行编号。 int count = 1; File picFile = new File(dir, ip + "(" + count + ").jpg"); while (picFile.exists()) { count++; picFile = new File(dir, ip + "(" + count + ").jpg"); } FileOutputStream fos = new FileOutputStream(picFile); 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) { } }
11 Http
演示二:
客户端:浏览器。
服务端:自定义的服务端 myServer
看到了如下信息:
HTTP的请求消息:请求头+请求体,中间有空行。
GET /myweb/1.html HTTP/1.1 // 请求行:请求方式 访问资源的路径 HTTP协议版本。
Accept: application/x-shockwave-flash, image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: 192.168.1.223:9090
Connection: Keep-Alive
空行
请求体
演示三:模拟一个浏览器。
客户端:自定义的浏览器。
服务端:Tomcat
发送和IE相同的HTTP协议的消息,收到了Tomcat服务器返回的数据。
HTTP的应答消息:
HTTP/1.1 200 OK //应答行 http协议版本 应答状态码 应答描述信息
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"467-1374891778953"
Last-Modified: Sat, 27 Jul 2013 02:22:58 GMT
Content-Type: text/html
Content-Length: 467
Date: Sun, 01 Sep 2013 07:26:09 GMT
Connection: close
空行
应答体
我自己定义的服务器
public static void main(String[] args) throws IOException { /* * 自定义server 服务端 * 获取浏览器的信息。并反馈信息。 */ System.out.println("my server run...."); 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(); }
模拟客户端
/* * 模拟一个浏览器。发送之前IE发送的http消息。 */ Socket s = new Socket("192.168.1.223",8080); //把IE的信息发送给服务端。 PrintWriter out = new PrintWriter(s.getOutputStream(),true); out.println("GET /myweb/3.html HTTP/1.1"); out.println("Accept: */*"); out.println("Host: 192.168.1.223:8080"); out.println("Connection: close"); out.println();//空行。 //读取服务端的数据。 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); s.close();
12 URL对象
Url常用方法和URLConnection
*/ public static void main(String[] args) throws IOException { String str_url = "http://192.168.1.223:8080/myweb/2.html"; //将url地址封装成对象。 URL url = new URL(str_url); // System.out.println("getProtocol:"+url.getProtocol()); // System.out.println("getHost:"+url.getHost()); // System.out.println("getPort:"+url.getPort()); // System.out.println("getPath:"+url.getPath()); // System.out.println("getFile:"+url.getFile()); // System.out.println("getQuery:"+url.getQuery()); //获取指定资源的连接对象。//封装了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); }
13 HTTP1.0和HTTP1.1区别
HTTP1.0:一次连接一次请求。
HTTP1.1:一次连接多次请求。
14 常见的网络架构
C/S: client / server
特点:
1,程序员需要开发客户端和服务端。
2,维护较为麻烦。
3,将一部分运算转移到客户端来完成,减轻服务器端的压力。
B/S: browser / server
特点:
1,程序员只需要开发服务端。客户端使用系统已有的浏览器即可。
2,维护很简单。只需要维护服务端。
3,所有的运算的都在服务端。
目前流行BS
作者:8亩田
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.
本文如对您有帮助,还请多帮 【推荐】 下此文。
如果喜欢我的文章,请关注我的公众号
如果有疑问,请下面留言