黑马程序员---网络编程(重点)

------- Java、.NetAndroid培训期待与您交流!------- 

一、网络模型

        OSI参考模型(开放式网络:7层体系结构)。

        TCP/IP参考模型(4层体系结构)。

二、网络通讯三要素

1、IP地址:InetAddress(找到对方IP地址):

        网络中设备的标识不易记忆,可用主机名。本地回环地址:127.0.0.1,主机名:localhost。

        InetAddress类:表示互联网协议 (IP) 地址,该类中都是对IP地址的操作。

        static InetAddress getLocalHost():返回本地主机。

        String getHostAddress():返回 IP 地址字符串(以文本表现形式)。 

        String getHostName():获取此 IP 地址的主机名。

        static InetAddress getByName(String host):获取网络上已连接的主机信息,可能只有IP地址而没有主机名,是因为该主机IP和主机名的映射关系未被注册到DNS服务器上。 

        注:对网络操作,获取IP地址最重要,即:getHostAddress 。

2、端口号或逻辑端口号:

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

        有效端口:0~65535,其中0~1024为系统使用或保留端。

3、传输协议(重点),常见协议:TCP和UDP

3.1、用户数据报协议 UDP:User Datagram Protocol

          面向无连接的,不可靠,只求速度数据包在64K以内。

          将数据以及源和目的封装在数据包中,不需要建立连接,速度块。

          如:视屏对话、桌面共享、某些聊天工具、步话机等。

3.2、传输控制协议 TCP:Transmission Control Protocol

          面向连接的,可靠,传输量大,速度慢。

          建立连接,形成传输数据的通道,通过三次握手完成连接,效率稍低。

          如:打电话。

          三次握手:

                    第一次:发送方A向接收方B发送数据,等待B接收到数据后返回的确认信息。

                    第二次:B接收到数据,返回给A一个接收到数据的信息,若B未接收到任何数据则不会发送任何信息。

                    第三次:若A接到到B的确认信息,则发送下一条数据。若A在一定时间内未接收到B返回的信息(可能B未发送信息或者B发送了信息但是在网络传输中出现了延迟或丢失),则认为是刚才发送的数据丢失,再次重新发送,直到收到B的确认信息。

三、Socket套接字

        1、网络编程即socket编程,是两台机器间通信的端点。

        2、通信的两端都有Socket。

        3、数据在两个Socket间通过IO流传输。

四、UDP和TCP编码流程(记住流程)

1、UDP数据包传输

      UDP发送端:

      示例:通过udp传输方式,把键盘录入将文字数据发送出去。

      思路:
            1,建立Updsocket服务。
            2,通过输入流,录入数据,并将数据封装到数据包中。
            3,通过socket服务的发送功能,将数据包发出去。

            4,关闭资源。

import java.net.*;
import java.io.*;

class UdpSend 
{
	public static void main(String[] args) throws Exception
	{
		//1、建立UDP服务,通过DatagramSocket对象建立。(可以对对象指定一个端口进行监听)
		DatagramSocket ds = new DatagramSocket(8888);
		//2、通过输入流输入数据,并将数据封装到数据包中
		BufferedReader bufIn = new  BufferedReader(new InputStreamReader(System.in));
		String line = null;
		while ((line=bufIn.readLine())!=null)
		{
			if ("886".equals(line))
				break;
			byte[] buf = line.getBytes();
			DatagramPacket dp = 
				new DatagramPacket(buf,buf.length,InetAddress.getByName("psy"),10000);
			//3,通过socket服务,将已有的数据包发送出去。通过send方法。
			ds.send(dp);
		}
		//4、关闭资源
		ds.close();
	}
}

      示例:定义一个应用程序,用于接收udp协议传输的数据,并把处理后的信息返回给发送者。

      思路:

            1,定义UdpSocket服务。通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识。方便于明确哪些数据过来该应用程序可以处理。

            2,定义一个数据包,要存储接收到的字节数据。因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。

            3,通过socket服务的receive方法将收到的数据存入已定义好的数据包中。

            4,通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。

            5,关闭资源,接收端的资源一般不关。

import java.net.*;
import java.io.*;

class UdpReceive
{
	public static void main(String[] args) throws Exception
	{
		//1、创建UdpSocket,建立端点,监听端口10000
		DatagramSocket ds = new DatagramSocket(10000); 
		while (true)
		{
			//2、定义数据包,用于存储数据
			byte[] buf = new byte[1024];
			DatagramPacket dp = new DatagramPacket(buf,buf.length);
			//3、通过服务的receive方法将首的数据存入数据包中
			ds.receive(dp);		//阻塞方法
			//4、通过数据包方法获取其中的数据
			String ip = dp.getAddress().getHostAddress();
			String data = new String(dp.getData(),0,dp.getLength());
			System.out.println(ip+"..."+data.toUpperCase());
		}
		//5、关闭资源
		ds.close();
	}
}

2、TCP传输
      1>tcp分客户端和服务端。
      2>客户端对应的对象是Socket,服务端对应的对象是ServerSocket。

      客户端:通过查阅Socket对象,发现在该对象建立时,就可以去连接指定主机。因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,在该通道进行数据的传输。

      需求:建立一个文本转换服务器。客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。

      分析:既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
            源:键盘录入。
            目的:网络设备,网络输出流。
            而且操作的是文本数据。可以选择字符流。

      步骤:
            1,建立服务。
            2,获取键盘录入。
            3,将数据发给服务端。
            4,获取服务端返回的大写数据。
            5,结束,关资源。

      都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。

import java.io.*;
import java.net.*;

class  TransClient
{
	public static void main(String[] args) throws Exception
	{
		//创建客户端的Socket服务,并指定目的的主机和端口
		Socket s = new Socket("psy",10002);
		//定义读取键盘数据的流对象。
		BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
		//为了发送数据,应该获取Socket流中的输出流。
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		//定义一个Socket读取流,读取服务端返回的信息
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		
		String line = null;
		while ((line=bufr.readLine())!=null)
		{
			if ("over".equals(line))
				break;
			//发送数据。
			out.println(line);

			String str = bufIn.readLine();
			System.out.println("server: "+str);
		}
		bufr.close();	//关闭键盘录入流
		s.close();		//关闭客户端
	}
}

      服务端:服务端通过ServerSocket来指定,并监听客户端需要访问的一个端口,达到建立服务端的目的,实现与客户端数据通信的基础。

      需求:建立一个文本转换服务器。客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。     

      分析:

            源:Socket读取流
            目的:Socket输出流
            都是文本,装饰一下。

      步骤:
            1,建立服务端的Socket服务。ServerSocket();并监听一个端口。
            2,获取连接过来的客户端对象。通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法是阻塞式的。
            3,客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据,并把大写数据返回给客户端。
            4,关闭服务端。(可选,一般不会关闭服务器)

import java.io.*;
import java.net.*;

class  TransServer{
	public static void main(String[] args) throws Exception{
		//1、建立服务端Socket服务,并监听一个端口。
		ServerSocket ss = new ServerSocket(10002);
		//2、通过accept方法获取连接过来的客户端对象。
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"...is connected!");
		//3、读取Socket读取流中的数据
		BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
		//目的:socket输出流。将大写数据写入到Socket输出流中,并放松给客户端。
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);

		String line = null;
		while ((line=bufIn.readLine())!=null)
		{			
			out.println(line.toUpperCase());
		}	
		s.close();		//关闭客户端
		ss.close();		//关闭服务端
	}
}

3、演示客户端和服务端。

3.1、客户端:浏览器 (telnet)

         服务端:自定义。

         自定义服务端。

ServerSocket ss = new ServerSocket(10006);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress());
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("<font color='red' size='8'>客户端你好</font>");//返回给telnet的信息
s.close();
ss.close();   

3.2、客户端:自定义。

         服务端:Tomcat服务器。

         自定义IE浏览器(客户端)。

Socket s = new Socket("127.0.0.1",8080);
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("GET /myweb/demo.html HTTP/1.1");
out.println("Accept: */*");
out.println("Accept-Language: zh-ch");
out.println("Host:psy:100"); //一定要有主机信息
out.println("Connection: closed"); 
//一定要有空行,保证请求消息头和请求数据体之间有一行空行
out.println();
BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while ((line=bufr.readLine())!=null){
         System.out.println(line);
}
s.close();

3.3、HTTP的请求消息头:浏览器在访问服务器时,向服务器发送的是HTTP请求,该请求信息中包含用户当前要访问服务端的信息内容,当服务端收到该HTTP请求时,会按照服务端浏览器支持的解压格式,将网页数据打包,再发送给服务端浏览器,这样做的目的是节省流量开支。

         HTTP的请求消息头:

GET /myweb/demo.html HTTP/1.1 //GET请求:客户端要访问/myweb/demo.html,通过HTTP协议1.1版本
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave
-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msw
ord, application/xaml+xml, application/x-ms-xbap, application/x-ms-application,
application/vnd.ms-xpsdocument  //可接收的文件类型(数据)
Accept-Language: zh-cn    //支持语言
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; QQDo
wnload 718; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET
 CLR 3.5.30729)      //用户信息
Accept-Encoding: gzip, deflate  //支持压缩格式(重要:提高效率)
Host: 127.0.0.1:10006    //访问主机的10006端口(一台服务器可能有多个主机)
Connection: Keep-Alive    //保持连接

            //请求消息头后是请求数据体,用一个空行隔开

         应带消息头(从服务器发出)

HTTP/1.1 200 OK      //200:相应状态码(代表成功:OK)
Server: Apache-Coyote/1.1   //服务器
Accept-Ranges: bytes
ETag: W/"269-1364666220000"   //一个标记
Last-Modified: Sat, 30 Mar 2013 17:57:00 GMT //被修改时间
Content-Type: text/html    //文本类型
Content-Length: 269     //大小269字节
Date: Sat, 30 Mar 2013 18:38:45 GMT
Connection: close

五、统一资源定位符 URL Uniform Resource Locater
1、URL对象常用方法

构造方法:URL(String protocol, String host, int port, String file)

      方法:String getFile() :获取此 URL 的文件名。 
                  String getHost() :获取此 URL 的主机名(如果适用)。 
                  String getPath() :获取此 URL 的路径部分。 
                  int getPort() :获取此 URL 的端口号。 
                  String getProtocol() :获取此 URL 的协议名称。 
                  String getQuery() :获取此 URL 的查询部,即文件名后的附加参数信息

                 URLConnection openConnection():连接到指定的主机。

                 URLConnection 抽象类将Socket对象封装且封有内置协议,所以简化了Socket方法,一句话搞定网络连接,同时该抽象类应用于应用层,会通过HTTP协议将HTTP消息头解析,所以将不会再显示消息头。

                  InputStream openStream():开流,即线连接再获取流,封装了openConnection().getInputStream()。但是一般情况我们还是会分步来做,因为openConnection()可获取连接对象,有更多的操作可以实现。

2、域名解析

      (1)http:\\127.0.0.1:8080/myweb/index.html

      (2)http:\\www.sina.com.cn

      问:两个URL在访问主机时做了什么事情?

      答:(1)127.0.0.1:8080因为是IP+端口,所以直接访问指定主机,没做其它事情。

              (2)www.sina.com.cn是一个主机名,那么它会先在本地C:\WINDOWS\system32\drivers\etc\hosts文件中找其与该主机名映射的IP地址,若找到则使用该IP;否则才会去公网上找一台域名解析服务器(该服务中有很多主机名和IP地址的映射关系)来解析该主机名对应的IP地址。

六、示例

      需求:客户端通过键盘录入用户名。
      服务端对这个用户名进行校验。

                  如果该用户存在,在服务端显示xxx,已登陆。并在客户端显示 xxx,欢迎光临。

                  如果该用户不存在,在服务端显示xxx,尝试登陆。并在客户端显示 xxx,该用户不存在。

                  最多就登录三次。

       客户端实现方式

import java.io.*;
import java.net.*;
//客户端
class LoginClient 
{
	public static void main(String[] args) throws Exception
	{
		//创建客户端的socket服务。指定目的主机和端口
		Socket s = new Socket("psy",10004);
		//创建输入流
		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 i=0; i<3; i++)
		{
			String line = bufr.readLine();
			if (line==null)
				break;	//跳出当前循环,执行循环体以后的内容
			if (line.equals(""))
			{
				System.out.println("用户名不能为空!");
				continue;//跳出当前循环,继续执行下一次循环
			}
			out.println(line);
			
			String info = bufIn.readLine();				
			System.out.println("客户端:"+info);
			if (info.contains("欢迎"))
				break;	
		}

		bufr.close();
		s.close();
	}
}   

      服务端实现方式

class LoginServer
{
	public static void main(String[] args) throws Exception
	{
		//创建服务端,监听端口10004
		ServerSocket ss = new ServerSocket(10004);
		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)  
                    break;
                //创建输入流关联用户文件   
                BufferedReader bufr = new BufferedReader(new FileReader("user.txt"));  
                //获取服务端输出流   
                PrintWriter out = new PrintWriter(s.getOutputStream(),true);  
  
                boolean flag = false;  
                String line = null;  
                while ((line=bufr.readLine())!=null){  
                    if (line.equals(name)){  
                        flag = true;  
                        break;  
                    }  
                }  
                if (flag){  
                    System.out.println("服务端:"+name+",已登录");  
                    out.println(name+",欢迎光临!");  
                    break;  //或者用s.close()代替break。     
                }  
                else{  
                    System.out.println("服务端:"+name+",尝试登录");  
                    out.println("用户名"+name+"不存在!");  
                }  
            }     
            System.out.println("关闭客户端链接");  
            s.close();  
        }         
        catch (Exception e){  
            throw new RuntimeException(ip+",校验失败");  
        }  
    }  
}  

       问:第15行和第32行的break都可达到一个目的跳出for循环执行关闭客户端语句,而且他们之中只要满足第一个break,就能跳出循环执行s.close() ,为什么还得在后面实现break?
       答:目的是节约资源,提高服务端性能。
               若只有第15行的break 语句,也能达到关闭客户端的目的。但是会导致即使满足了if (flag) 判断条件,还得要多执行一段时间,直到下一次读到第15行的break才跳出执行s.close() 。若用户太多的情况下,不利于服务端优化,高效释放资源。break存在的必要:为了跳出当前循环,以便关闭客户端,释放资源。

        总结:网络编程同IO流一样的,都是重中之重,重点掌握实现他们的步骤流程,建立在这个基础上再来处理具体的逻辑问题,就会简单很多,就好比刚建的新房,其他就只需做的好内部装饰就OK了,这就是我们现在处理问题的思路,步步深入。网络编程一般都会和IO流相搭配来完成对网络数据的操作,因为很多时候都要实现服务端与客户端的动态交互。

       该章重点掌握,特别是UDP和TCP传输协议的区别和应用,URL中的openConnection()方法的使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值