黑马程序员—【Java基础篇】之网络编程

------- android培训 java培训 、期待与您交流! ---------

    伙伴们,这篇分享网络编程,开始,跑,哈哈。

一、网络编程

1、网络模型

/**
(1)OSI参考模型
(2)TCP/IP参考模型
*/ 

    参考模型图:

2、网络通讯要素

    网络通讯要素主要包括:IP、端口号和传输协议。
/**
(1)IP地址:InetAddress
    a、网络中设备的标识
    b、不易记忆,可用主机名
    c、本地回环地址:127.0.0.1  主机名:localhost
(2)端口号
    a、用于标识进程的逻辑地址,不同进程的标识
    b、有效端口:0~65535,其中0~1024系统使用或保留端口。
(3)传输协议
    a、通讯的规则
    b、常见协议:TCP,UDP
*/

二、传输协议

1、概述

/**
传输协议
    a、通讯的规则
    b、常见协议:TCP,UDP
*/

2、Socket

<pre name="code" class="java">/**
1、定义
    Socket就是为网络服务提供的一种机制。通信的两端都有Socket。
2、特点
    a、网络通信其实就是Socket间的通信。
    b、数据在两个Socket间通过IO传输。
*/
 
 
 
 

3、TCP和UDP

<span style="font-size:14px;">/**
(1)UDP
    a、将数据及源和目的封装成数据包中,不需要建立连接。
    b、每个数据报的大小在限制在64k内因无连接,是不可靠协议
    c、不需要建立连接,速度快
(2)TCP
    a、建立连接,形成传输数据的通道。
    b、在连接中进行大数据量传输
    c、通过三次握手完成连接,是可靠协议
    d、必须建立连接,效率会稍低
*/</span>
  UDP传输协议示例:
 
 

/**
需求:通过udp传输方式,将一段文字数据发送出去。,
定义一个udp发送端。
思路:
(1)建立updsocket服务。
(2)提供数据,并将数据封装到数据包中。
(3)通过socket服务的发送功能,将数据包发出去。
(4)关闭资源。
*/
class  UdpSend
{
	public static void main(String[] args) throws Exception
	{
		//1,创建udp服务。通过DatagramSocket对象。
		DatagramSocket ds = new DatagramSocket(8888);

		//2,确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port) 

		byte[] buf = "udp ge men lai le ".getBytes();
		DatagramPacket dp = 
			new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);

		//3,通过socket服务,将已有的数据包发送出去。通过send方法。
		ds.send(dp);
		//4,关闭资源。
		ds.close();
	}
}
/**
需求:定义一个应用程序,用于接收udp协议传输的数据并处理的,定义udp的接收端。
思路:
    (1)定义udpsocket服务。
    通常会监听一个端口。其实就是给这个接收网络应用程序定义数字标识,方便于明确哪些数据过来该应用程序可以处理。
    (2)定义一个数据包,因为要存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
    (3)通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
    (4)通过数据包对象的特有功能。将这些不同的数据取出。打印在控制台上。
    (5)关闭资源。
*/


class  UdpRece
{
	public static void main(String[] args) throws Exception
	{
		//1,创建udp socket,建立端点。
		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());

		int port = dp.getPort();

		System.out.println(ip+"::"+data+"::"+port);

		}
		//5,关闭资源
		//ds.close();

	}
}
/**
总结:
(1)DatagramSocket与DatagramPacket;
(2)建立发送端,接收端;
(3)建立数据包;
(4)调用Socket的发送接收方法。
(5)关闭Socket。
    发送端与接收端是两个独立的运行程序。
*/<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">    </span>

   TCP传输协议示例:

/**
分析:
(1)Socket和ServerSocket
(2)建立客户端和服务器端
(3)建立连接后,通过Socket中的IO流进行数据的传输
(4)关闭socket
注意事项:客户端与服务器端是两个独立的应用程序。
/**
客户端,
通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机。因为tcp是面向连接的。所以在建立socket服务时,就要有服务端存在,并连接成功。形成通路后,在该通道进行数据的传输。
需求:给服务端发送给一个文本数据。
步骤:
(1)创建Socket服务。并指定要连接的主机和端口。
*/
import java.io.*;
import java.net.*;
class  TcpClient
{
	public static void main(String[] args) throws Exception 
	{
		//创建客户端的socket服务。指定目的主机和端口
		Socket s = new Socket("192.168.1.254",10003);
		
		//为了发送数据,应该获取socket流中的输出流。
		OutputStream out = s.getOutputStream();

		out.write("tcp ge men lai le ".getBytes());


		s.close();
	}
}
/**
需求:定义端点接收数据并打印在控制台上。

服务端:
(1)建立服务端的socket服务。ServerSocket();
	并监听一个端口。
(2)获取连接过来的客户端对象。
	通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。
(3)客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。
	并打印在控制台。

(4)关闭服务端。(可选)
*/
class  TcpServer
{
	public static void main(String[] args) throws Exception
	{
		//建立服务端socket服务。并监听一个端口。
		ServerSocket ss = new ServerSocket(10003);

		//通过accept方法获取连接过来的客户端对象。
		while(true)
		{
		Socket s = ss.accept();

		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+".....connected");

		//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据。
		InputStream in = s.getInputStream();
		
		byte[] buf = new byte[1024];
		int len = in.read(buf);

		System.out.println(new String(buf,0,len));

		s.close();//关闭客户端.
		}
		//ss.close();
	}
}
*/<span style="font-size:14px; font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">   </span>
    UDP传输应用练习:
/**
需求:编写一个聊天程序,有收数据的部分和发数据的部分,这两部分需要同时执行。
分析:那就需要用到多线程技术,一个线程控制收,一个线程控制发。
    因为收和发动作是不一致的,所以要定义两个run方法,而且这两个方法要封装到不同的类中。

*/
import java.io.*;
import java.net.*;
class Send implements Runnable
{
	private DatagramSocket ds;
	public Send(DatagramSocket ds)
	{
		this.ds = ds;
	}


	public void run()
	{
		try
		{
			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("192.168.1.255"),10002);

				ds.send(dp);

				if("886".equals(line))
					break;
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException("发送端失败");
		}
	}
}

class Rece implements Runnable
{

	private DatagramSocket ds;
	public Rece(DatagramSocket ds)
	{
		this.ds = ds;
	}
	public void run()
	{
		try
		{
			while(true)
			{
				byte[] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf,buf.length);

				ds.receive(dp);


				String ip = dp.getAddress().getHostAddress();

				String data = new String(dp.getData(),0,dp.getLength());

				if("886".equals(data))
				{
					System.out.println(ip+"....离开聊天室");
					break;
				}


				System.out.println(ip+":"+data);
			}
		}
		catch (Exception e)
		{
			throw new RuntimeException("接收端失败");
		}
	}
}


class  ChatDemo
{
	public static void main(String[] args) throws Exception
	{
		DatagramSocket sendSocket = new DatagramSocket();
		DatagramSocket receSocket = new DatagramSocket(10002);

		new Thread(new Send(sendSocket)).start();
		new Thread(new Rece(receSocket)).start();

	}
}
   
     TCP传输应用练习:
import java.io.*;
import java.net.*;
/**
需求:
    演示tcp的传输的客户端和服务端的互访,客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
客户端:
(1)建立socket服务。指定要连接主机和端口。
(2)获取socket流中的输出流。将数据写到该流中。通过网络发送给服务端。
(3)获取socket流中的输入流,将服务端反馈的数据获取到,并打印。
(4)关闭客户端资源。
*/<pre name="code" class="java">class TcpClient2 
{
	public static void main(String[] args)throws Exception 
	{
		Socket s = new Socket("192.168.1.254",10004);
		
		OutputStream out = s.getOutputStream();

		out.write("服务端,你好".getBytes());	
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));

		s.close();
	}
}
class TcpServer2
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss = new ServerSocket(10004);
		Socket s = ss.accept();
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip+"....connected");
		InputStream in = s.getInputStream();
		byte[] buf = new byte[1024];
		int len = in.read(buf);
		System.out.println(new String(buf,0,len));
		OutputStream out = s.getOutputStream();
		Thread.sleep(10000);
		out.write("哥们收到,你也好".getBytes());
		s.close();
		ss.close();
	}
}
 
 

4、应用

    应用1:
/* *
需求:并发上传图片 
*/  
import java.io.*;  
import java.net.*;  
//客户端  
class  PicClient  
{  
    public static void main(String[] args) throws Exception  
    {  
        //对传入的值进行判断  
        if (args.length!=1)  
        {  
            System.out.println("请指定一个图片文件!");  
            return;  
        }  
  
        File file=new File(args[0]);  
  
        //对文件路径进行判断  
        if (!(file.exists()&&file.isFile()))  
        {  
            System.out.println("你上传的文件有问题,非文件或者不存在!");  
            return;  
        }  
  
        //判断是否是图片文件  
        if (!file.getName().endsWith(".jpg"))  
        {  
            System.out.println("图片格式错误,请重新选择!");  
            return;  
        }  
  
        //对文件大小进行判断  
        if (file.length()>1024*1024*5)  
        {  
            System.out.println("你上传的文件过大,居心叵测!");  
            return;  
        }  
  
        //创建服务  
        Socket s=new Socket("localhost",10000);  
        //读取图片数据  
        FileInputStream fis=new FileInputStream(file);  
          
        //用Socket服务输出流写入数据  
        OutputStream out =s.getOutputStream();  
  
        BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  
        byte[] buf=new byte[1024];  
  
        int len=0;  
  
        while ((len=fis.read(buf))!=-1)  
        {  
            out.write(buf,0,len);  
        }  
  
        //结束标记,表示文件数据已经上传完了  
        s.shutdownOutput();  
  
        String info=in.readLine();//读取返回信息  
        System.out.println(info);  
  
        fis.close();//关流  
        s.close();  
  
    }  
}  
  
//服务端  
class PicServer  
{  
    public static void main(String[] args)throws Exception  
    {  
        //创建服务,监听端口  
        ServerSocket ss=new ServerSocket(10000);  
          
        while (true)  
        {  
            //获取客户端对象  
            Socket s=ss.accept();  
            //客户端执行线程  
            new Thread(new PicThread(s)).start();  
        }  
          
        //ss.close();  
    }  
}  
  
//利用多线程实现并发上传  
class PicThread implements Runnable  
{  
    private Socket s;  
    PicThread(Socket s)  
    {  
        this.s=s;  
    }  
    public void run()  
    {  
        int count=1;  
        //获取客户端ip  
        String ip=s.getInetAddress().getHostAddress();  
        try  
        {         
            System.out.println(ip+"  connected.....");  
  
            //通过客户端的读取流读取数据  
            InputStream in=s.getInputStream();  
            //文件保存路径  
            File dir =new File("C:\\Users\\asus\\Desktop");  
            //文件名  
            File file=new File(dir,ip+".jpg");  
            //判断文件是否存在  
            while(file.exists())  
            {  
                file=new File(dir,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+"图片上传失败");  
        }  
    }  
} <pre name="code" class="java">小结:
1、一对一(单线程)上传的思路:
客户端
        a、服务端点。
        b、读取客户端已有的图片数据
        c、通过Socket输出流将数据发给服务端
        d、读取服务端反馈信息。
        e、关闭
服务端
        a、服务端服务,并监听窗口
        b、获取客户端对象,并获取客户ip
        c、读取客户端输入流数据
        d、写入文件
        e、用客户端输出流反馈信息
        f、关流
2、单线程的服务端有个局限性。
    当A客户端连接上以后,被服务端获取到。服务端执行具体流程。这时B客户端连接,只能等待。因为服务端还没有处理完A客户端的请求。还没有循环回来执行下一次accept方法。所以,暂时获取不到B客户端对象。
        那么为了可以让多个客户端同时并发访问服务端。服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
        只要明确了每一个客户端要在服务端执行的代码,如示例所示将该代码存入run方法即可。
*/<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
 
     应用2: 
 
import java.io.*;  
import java.net.*;  
//客户端  
class  LoginClient  
{  
    public static void main(String[] args) throws Exception  
    {  
        //创建服务  
        Socket s=new Socket("localhost",10000);  
        //键盘录入  
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
  
        //用Socket服务输出流写入数据  
        PrintWriter out =new PrintWriter(s.getOutputStream(),true );  
  
        //接收服务器返回的信息  
        BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  
  
        String line=null;  
  
        for(int x=0;x<3;x++)  
        {  
            line=br.readLine();//读取键盘录入  
            if (line==null)  
            {  
                break;//如果键盘没有输入,则直接结束  
            }  
  
            out.println(line);//将数据写入流中  
  
            String info=in.readLine();//读取返回信息  
  
            System.out.println(info);  
  
            if (info.contains("欢迎"))//---------------  
            {  
                break;//如果登录成功,就跳出循环  
            }  
        }  
        br.close();//关流  
        s.close();  
    }  
}  
  
//服务端  
class LoginServer  
{  
    public static void main(String [] args)throws Exception  
    {  
        //创建服务,监听端口  
        ServerSocket ss=new ServerSocket(10000);  
          
        while (true)  
        {  
            //获取客户端对象  
            Socket s=ss.accept();  
          
            //客户端执行线程  
            new Thread(new LoginThread(s)).start();  
        }  
          
        //ss.close();  
    }  
}  
  
//利用多线程实现并发登录  
class LoginThread implements Runnable  
{  
    private Socket s;  
    LoginThread(Socket s)  
    {  
        this.s=s;  
    }  
    public void run()  
    {  
        //获取客户端ip  
        String ip=s.getInetAddress().getHostAddress();  
        System.out.println(ip+"  connected.....");  
        try  
        {         
            for (int x=0;x<3 ;x++ )  
            {     
                //通过客户端的读取流读取数据  
                BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));  
                  
                //读取数据库中的数据,这里用文件来表示数据库  
                BufferedReader br=new BufferedReader(new FileReader("users.txt"));  
  
                String line=in.readLine();//读取客户端数据  
                if (line==null)//--------------  
                {  
                    break;//如果客户端没有发送数据,则跳出循环  
                }  
                String data=null;  
                boolean flag=false;//设置标记  
                //读取数据库中的用户数据  
                while ((data=br.readLine())!=null)  
                {  
                    if (line.equals(data))  
                    {  
                        flag=true;//如果用户存在,则将标记设为true  
                        break;  
                    }  
                }  
  
                //将数据写入到指定文件中  
                PrintWriter out=new PrintWriter(s.getOutputStream(),true);  
  
                if (flag)  
                {  
                    System.out.println(line+",已登陆!");  
                      
                    out.println(line+",欢迎光临!");  
  
                    break;//-----------  
                }  
                else  
                {  
                    System.out.println(line+",尝试登陆!");  
                    out.println(line+",用户名不存在!");  
                }     
            }  
            s.close();//关流  
        }  
        catch (Exception e)  
        {  
            throw new RuntimeException("用户登陆失败");  
        }     
    }  
}<pre name="code" class="java">/**
小结:
    (1)浏览器是一个标准的客户端,它可以对服务端传送过来的数据消息进行解析,把符合应用层协议的消息部分解析后,将头信息拆包掉,传送到应用层,只保留了正确的正文主题部分显示在主体部分上。
    (2)而由于使用java编译是在传输层和网际层处理的,所以,会接受到全部的消息,包含了头消息。而浏览器处于应用层,已将发送来的头消息去除,只留下了主体信息。
*/<span style="font-family: Arial, Helvetica, sans-serif;">  </span>
 
 

三、URL和URLConnection

1、概

/**
(1)URL:
        URI:范围更大,条形码也包含于此范围
        URL:范围较小,即域名
1)方法
        a、构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocol、host、port号和 file 创建 URL对象。
        b、String getProtocol();//获取协议名称
        c、String getHost();//获取主机名
        d、int getPort();//获取端口号
        e、String getFile();//获取URL文件名
        f、String getPath();//获取此URL的路径部分
       g、String getQuery();//获取此URL的查询部,客户端传输的特定信息
注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port为-1,则分配一个默认的80端口,如
        int port = getPort();
        if(port == -1)
              port = 80;
(2)URLConnection
1)方法:
        a、URLConnection openConnection();//用URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
        b、InputStream getInputStream();//获取输入流
        c、OutputStream getOutputStream();//获取输出流
*/

     练习1:

/* *
自定义浏览器,显示网页信息 
*/  
  
import java.io.*;  
import java.awt.*;  
import java.awt.event.*;  
import java.net.*;  
  
class MyIEGUIDemo  
{  
    //定义所需组件引用  
    private Frame f;  
    private Button but,bok;  
    private TextField tf;  
    private TextArea ta;  
  
    //构造函数  
    MyIEGUIDemo()  
    {  
        init();  
    }  
  
    //窗体基本设置于功能实现  
    public void init()  
    {  
        //组件实例化  
        f=new Frame("我的Window");  
        but=new Button("跳转");  
        tf=new TextField(50);  
        ta=new TextArea(25,60);  
  
        //基本设置  
        f.setBounds(300,150,500,500);  
        f.setLayout(new FlowLayout());  
  
        //添加组件  
        f.add(tf);  
        f.add(but);  
        f.add(ta);  
  
        //窗体事件  
        myEvent();  
  
        //窗体显示  
        f.setVisible(true);  
    }  
  
    //注册事件  
    public void myEvent()  
    {  
        //窗体关闭功能  
        f.addWindowListener(new WindowAdapter()  
        {  
            public void windowClosing(WindowEvent e)  
            {  
                System.exit(0);  
            }  
        });  
  
        //“跳转”按钮事件  
        but.addActionListener(new ActionListener()  
        {  
            public void actionPerformed(ActionEvent e)  
            {  
                showFile();//显示网页内容在文本区中  
            }  
        });  
  
          
  
        //文本框键盘事件  
        tf.addKeyListener(new KeyAdapter()  
        {  
            public void keyPressed(KeyEvent e)  
            {  
                //如果键盘按下Enter键,就将网页内容显示在文本区中  
                if(e.getKeyCode()==KeyEvent.VK_ENTER)  
                    showFile();  
            }  
        });  
    }  
  
    //显示网页内容  
        private void showFile()  
        {  
            ta.setText("");  
            String path=tf.getText();//获取输入的路径  
            try  
            {  
                //封装地址对象  
URL url =new URL(path);  
连接网页服务器  
                URLConnection conn=url.openConnection();  
                //读取流,用于读取服务器返回数据  
                InputStream in=conn.getInputStream();  
  
                byte[] buf=new byte[1024*1024];  
  
                int len=in.read(buf);  
                //将数据显示在文本区中  
                ta.append(new String(buf,0,len));  
            }  
            catch (Exception e)  
            {  
                throw new RuntimeException("连接"+path+"网站失败");  
            }  
        }  
  
    public static void main(String[] args)   
    {  
        //运行窗体  
        new MyIEGUIDemo();  
    }  
}  

2、小知识点扩展

/**
(1)InetSocketAddress对象(IP+端口)
(2)ServerSocket对象中的构造函数:
    ServerSocket(int port,int backlog),其中的backlog表示队列的最大长度,即最多连入客户端的个数,即最大连接数。
(3)在进行浏览器输入网址访问一台主机所做的操作:
    如http://192.168.1.100:8080/myweb/myIE.html,一般直接输入主机名:http://baidu.com.cn等,那么如何通过主机名获取IP地址,从而连接到这台主机的呢?这就需要将主机名翻译成IP地址,即域名解析:DNS。
    在进行访问的时候,会现在本地的hosts文件(C:\WINDOWS\system32\drivers\etc\hosts)中找对应的映射,若有,则直接返回请求,若无,则到公网的映射列表即DNS中找对应的映射,找到后,将主机名对应的IP地址返回给本机,本机通过这个IP地址找到对应的服务器。
*/
  
    图片说明:


    host的应用:
    (1)可屏蔽一些恶意网址,即将对应的映射关系写入hosts中,将IP地址改为本机的回环地址,那么会直接找到hosts,就不会将请求发送出去了。
    (2)可以提高上网速度。
    好了,大神们,现在是下午17:11分,肚子有些饿了,准备晚餐去了,有一起的吗?那就吃饭饭了,嘎嘎。



------- android培训 java培训 、期待与您交流! ---------


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值