java网络编程

网络基础知识

(2) 每个计算机设备上都有若干个网卡
(3) 每个网卡都有单独唯一全球的mac地址
(4) ipv4地址与ipv6地址的区别在于:ipv6这个地址是在全球独一无二的,ipv4在局域网内你的ip地址是独一无二的!
(5) 请求一台服务器需要两个东西,一个是ip另一个是端口,我们电脑上有65535个端口,0-1023的端口已经被我们系统使用,
(6)tracert可以查看当前机器与目标机器的访问中继(也就是我们和我们访问的目标地址隔了多少网络)
(7)通讯协议:TCP和UDP
TCP:双向字节传递,稳定(建立连接3次握手),对网络的要求比较高
UDP: 不稳定,不保证可靠数据的传输,但速度快(腾讯qq使用UDP)
(8)网络分为5层:
应用层–>运输层–>网络层–>链路层–>物理层,这五层我到时候会专门开一篇网络博客来仔细讲解

UDP编程

1 计算机通讯:数据从一个ip的port出发(发送方),运输到另一个ip的port(接收方),UDP为无连接,无状态的通讯协议:
(1) 发送方发送消息,如果接收方刚好在目的地,则可以接受,如果不在,那这个消息就丢失了
(2) 发送方无法得知发送是否成功
2 在java中,UDP编程主要由两个类辅助:
(1) DatagramSocket:通讯的数据管道
- -send和receive方法
- -可选多网卡,绑定一个Ip和port
(2) DatagramPacket
– 集装箱:封装数据
–地址标签:目的地IP+port
3 下面UDP的例子编译后运行,然后就可以在命令行中看到效果
接收方

import java.net.*;

public class Send {
    public static void main(String[] args) throws Exception {
        //通讯的数据管道
        DatagramSocket ds = new DatagramSocket();
        String str = "你好,我是发送者";
        //绑定id和端口,并且将输入内容打包
        DatagramPacket dp = new DatagramPacket(str.getBytes(), str.length(),
                InetAddress.getByName("127.0.0.1"), 3000);

        //发送消息
        ds.send(dp);
        System.out.println("Send: 我发送信息结束");
       //睡眠1秒钟
        Thread.sleep(1000);
        byte[] buf = new byte[1024];
        //新建一个集装箱封装类,等待返回消息
        DatagramPacket dp2 = new DatagramPacket(buf, 1024);
        System.out.println("Send: 我在等待信息");
        ds.receive(dp2);
        System.out.println("Send: 我接收到信息");
        String str2 = new String(dp2.getData(), 0, dp2.getLength()) +
                " from " + dp2.getAddress().getHostAddress() + ":" + dp2.getPort();
        System.out.println(str2);

        ds.close();
    }
}

发送方


import java.net.*;
public class Recv
{
	public static void main(String[] args) throws Exception
	{   //同发送方
		DatagramSocket	ds=new DatagramSocket(3000);
		byte [] buf=new byte[1024];
		DatagramPacket dp=new DatagramPacket(buf,1024);
		
		System.out.println("Recv: 我在等待信息");
		ds.receive(dp);
		System.out.println("Recv: 我接收到信息");
		String strRecv=new String(dp.getData(),0,dp.getLength()) +
		" from " + dp.getAddress().getHostAddress()+":"+dp.getPort(); 
		System.out.println(strRecv);
		
		Thread.sleep(1000);
		System.out.println("Recv: 我要发送信息");
		String str="hello world 222";
		DatagramPacket dp2=new DatagramPacket(str.getBytes(),str.length(), 
				InetAddress.getByName("127.0.0.1"),dp.getPort());
		ds.send(dp2);
		System.out.println("Recv: 我发送信息结束");
		ds.close();
	}
}

TCP编程

TCP协议: 有链接,保证可靠的无误差通讯
1 服务器:创建一个serverSocket,等待连接
2 客户机:创建一个socket,连接到服务器
3 服务器:serverSocket接收到连接,创建一个socket和客户的socket专线连接,后续的客户机和服务器会在一个单独的线程上运行.
4 服务器继续等待连接
serverSocket: 服务器码头
–需要绑定port
–如果有多块网卡,需要绑定一个ip地址
–客户端需要绑定服务器的地址和端口
–客户端往Socket输入流写数据,送到服务端
–服务端等待响应时处于阻塞状态
–服务端可以同时响应多个客户端
–服务端每接受一个客户端,就启动一个独立的线程与之对应
–客户端和服务端都可以选择关闭对Socket的实例
–服务端先启动,且一直保留

客户端

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

public class TcpClient {
	public static void main(String[] args) {
		try {
			Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8001); //需要服务端先开启
			
			//同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流
			InputStream ips = s.getInputStream();    //开启通道的输入流
			BufferedReader brNet = new BufferedReader(new InputStreamReader(ips));
			
			OutputStream ops = s.getOutputStream();  //开启通道的输出流
			DataOutputStream dos = new DataOutputStream(ops);			

			BufferedReader brKey = new BufferedReader(new InputStreamReader(System.in));
			while (true) 
			{
				String strWord = brKey.readLine();
				if (strWord.equalsIgnoreCase("quit"))
				{
					break;
				}
				else
				{
					System.out.println("I want to send: " + strWord);
					dos.writeBytes(strWord + System.getProperty("line.separator"));
					
					System.out.println("Server said: " + brNet.readLine());
				}
				
			}
			
			dos.close();
			brNet.close();
			brKey.close();
			s.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

服务端

 
import java.net.*;
import java.io.*;
public class TcpServer
{
	public static void main(String [] args) 
	{
		try
		{
			ServerSocket ss=new ServerSocket(8001); //驻守在8001端口
			Socket s=ss.accept();                   //阻塞,等到有客户端连接上来
			System.out.println("welcome to the java world");
			InputStream ips=s.getInputStream();     //有人连上来,打开输入流
			OutputStream ops=s.getOutputStream();   //打开输出流
			//同一个通道,服务端的输出流就是客户端的输入流;服务端的输入流就是客户端的输出流
			
			ops.write("Hello, Client!".getBytes());  //输出一句话给客户端
			
			
			BufferedReader br = new BufferedReader(new InputStreamReader(ips));
			//从客户端读取一句话			
			System.out.println("Client said: " + br.readLine());
			
			
			ips.close();          
			ops.close();
			s.close();
			ss.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}

服务端2,每次有一个客户端连接上来以后就重新开一个线程,此代码案例中无线程池管理,但如果真正应用的话一定需要用线程池管理,因为如果一味的开线程的话,服务器会崩!
线程功能实现类:

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

class Worker implements Runnable {
	Socket s;

	public Worker(Socket s) {
		this.s = s;
	}

	public void run() {
		try {
			System.out.println("服务人员已经启动");
			InputStream ips = s.getInputStream();
			OutputStream ops = s.getOutputStream();

			BufferedReader br = new BufferedReader(new InputStreamReader(ips));
			DataOutputStream dos = new DataOutputStream(ops);
			while (true) {
				String strWord = br.readLine();
				System.out.println("client said:" + strWord +":" + strWord.length());
				if (strWord.equalsIgnoreCase("quit"))
					break;
				String strEcho = strWord + " 666";
				// dos.writeBytes(strWord +"---->"+ strEcho +"\r\n");
				System.out.println("server said:" + strWord + "---->" + strEcho);
				dos.writeBytes(strWord + "---->" + strEcho + System.getProperty("line.separator"));
			}
			br.close();
			// 关闭包装类,会自动关闭包装类中所包装的底层类。所以不用调用ips.close()
			dos.close();
			s.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public class TcpServer2
{
	public static void main(String [] args)
	{
		try
		{
			ServerSocket ss=new ServerSocket(8001);
			while(true)
			{
				Socket s=ss.accept();
				System.out.println("来了一个client");
				new Thread(new Worker(s)).start();
			}
			//ss.close();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

}

http编程

1 访问方式
–GET 从服务器获取资源到客户端
–POST 从客户端向服务器发送数据
–PUT 上传文件
–DELETE 删除文件
–HEAD 报文头部
–OPTIONS 询问支持的方法
–TRACE 追踪路径
–CONNECT 用隧道协议连接代理
基础案例
GET


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


public class URLConnectionGetTest
{
   public static void main(String[] args)
   {
      try
      {
         String urlName = "http://www.baidu.com";

         URL url = new URL(urlName);
         URLConnection connection = url.openConnection(); 
         connection.connect();

         // 打印http的头部信息

         Map<String, List<String>> headers = connection.getHeaderFields();
         for (Map.Entry<String, List<String>> entry : headers.entrySet())
         {
            String key = entry.getKey();
            for (String value : entry.getValue())
               System.out.println(key + ": " + value);
         }

         // 输出将要收到的内容属性信息

         System.out.println("----------");
         System.out.println("getContentType: " + connection.getContentType());
         System.out.println("getContentLength: " + connection.getContentLength());
         System.out.println("getContentEncoding: " + connection.getContentEncoding());
         System.out.println("getDate: " + connection.getDate());
         System.out.println("getExpiration: " + connection.getExpiration());
         System.out.println("getLastModifed: " + connection.getLastModified());
         System.out.println("----------");

         BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));

         // 输出收到的内容
         String line = "";
         while((line=br.readLine()) != null)
         {
        	 System.out.println(line);
         }
         br.close();
      }
      catch (IOException e)
      {
         e.printStackTrace();
      }
   }
}
   

POST


import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;

public class URLConnectionPostTest
{
   public static void main(String[] args) throws IOException
   {
      String urlString = "https://tools.usps.com/go/ZipLookupAction.action";
      Object userAgent = "HTTPie/0.9.2";
      Object redirects = "1";
      CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));
      
      Map<String, String> params = new HashMap<String, String>();
      params.put("tAddress", "1 Market Street");  
      params.put("tCity", "San Francisco");
      params.put("sState", "CA");
      String result = doPost(new URL(urlString), params, 
         userAgent == null ? null : userAgent.toString(), 
         redirects == null ? -1 : Integer.parseInt(redirects.toString()));
      System.out.println(result);
   }   

   public static String doPost(URL url, Map<String, String> nameValuePairs, String userAgent, int redirects)
         throws IOException
   {        
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
      if (userAgent != null)
         connection.setRequestProperty("User-Agent", userAgent);
      
      if (redirects >= 0)
         connection.setInstanceFollowRedirects(false);
      
      connection.setDoOutput(true);
      
      //输出请求的参数
      try (PrintWriter out = new PrintWriter(connection.getOutputStream()))
      {
         boolean first = true;
         for (Map.Entry<String, String> pair : nameValuePairs.entrySet())
         {
        	//参数必须这样拼接 a=1&b=2&c=3
            if (first) 
            {
            	first = false;
            }
            else
            {
            	out.print('&');
            }
            String name = pair.getKey();
            String value = pair.getValue();
            out.print(name);
            out.print('=');
            out.print(URLEncoder.encode(value, "UTF-8"));
         }
      }      
      String encoding = connection.getContentEncoding();
      if (encoding == null) 
      {
    	  encoding = "UTF-8";
      }
            
      if (redirects > 0)
      {
         int responseCode = connection.getResponseCode();
         System.out.println("responseCode: " + responseCode);
         if (responseCode == HttpURLConnection.HTTP_MOVED_PERM 
               || responseCode == HttpURLConnection.HTTP_MOVED_TEMP
               || responseCode == HttpURLConnection.HTTP_SEE_OTHER) 
         {
            String location = connection.getHeaderField("Location");
            if (location != null)
            {
               URL base = connection.getURL();
               connection.disconnect();
               return doPost(new URL(base, location), nameValuePairs, userAgent, redirects - 1);
            }
            
         }
      }
      else if (redirects == 0)
      {
         throw new IOException("Too many redirects");
      }
      
      //接下来获取html 内容
      StringBuilder response = new StringBuilder();
      try (Scanner in = new Scanner(connection.getInputStream(), encoding))
      {
         while (in.hasNextLine())
         {
            response.append(in.nextLine());
            response.append("\n");
         }         
      }
      catch (IOException e)
      {
         InputStream err = connection.getErrorStream();
         if (err == null) throw e;
         try (Scanner in = new Scanner(err))
         {
            response.append(in.nextLine());
            response.append("\n");
         }
      }

      return response.toString();
   }
}

基于org.apache下的HttpComponents包下的GET请求和POST请求


import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpComponentsGetTest {

    public final static void main(String[] args) throws Exception {
    	
    	CloseableHttpClient httpClient = HttpClients.createDefault();
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000)   //设置连接超时时间
                .setConnectionRequestTimeout(5000) // 设置请求超时时间
                .setSocketTimeout(5000)
                .setRedirectsEnabled(true)//默认允许自动重定向
                .build();
        
        HttpGet httpGet = new HttpGet("http://www.baidu.com");
        httpGet.setConfig(requestConfig);
        String srtResult = "";
        try {
            HttpResponse httpResponse = httpClient.execute(httpGet);
            if(httpResponse.getStatusLine().getStatusCode() == 200){
                srtResult = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");//获得返回的结果                
                System.out.println(srtResult);
            }else
            {
                //异常处理
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class HttpComponentsPostTest {

    public final static void main(String[] args) throws Exception {
    	
    	//获取可关闭的 httpCilent
        //CloseableHttpClient httpClient = HttpClients.createDefault();
    	CloseableHttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
    	//配置超时时间
        RequestConfig requestConfig = RequestConfig.custom().
                setConnectTimeout(10000).setConnectionRequestTimeout(10000)
                .setSocketTimeout(10000).setRedirectsEnabled(false).build();
         
        HttpPost httpPost = new HttpPost("https://tools.usps.com/go/ZipLookupAction.action");
        //设置超时时间
        httpPost.setConfig(requestConfig);
        
        //装配post请求参数
        List<BasicNameValuePair> list = new ArrayList<BasicNameValuePair>(); 
        list.add(new BasicNameValuePair("tAddress", URLEncoder.encode("1 Market Street", "UTF-8")));  //请求参数
        list.add(new BasicNameValuePair("tCity", URLEncoder.encode("San Francisco", "UTF-8"))); //请求参数
        list.add(new BasicNameValuePair("sState", "CA")); //请求参数
        try {
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list,"UTF-8"); 
            //设置post求情参数
            httpPost.setEntity(entity);
            httpPost.setHeader("User-Agent", "HTTPie/0.9.2");
            //httpPost.setHeader("Content-Type","application/form-data");
            HttpResponse httpResponse = httpClient.execute(httpPost);
            String strResult = "";
            if(httpResponse != null){ 
                System.out.println(httpResponse.getStatusLine().getStatusCode());
                if (httpResponse.getStatusLine().getStatusCode() == 200) {
                    strResult = EntityUtils.toString(httpResponse.getEntity());
                }
                else {
                    strResult = "Error Response: " + httpResponse.getStatusLine().toString();
                } 
            }else{
                 
            }
            System.out.println(strResult);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(httpClient != null){
                    httpClient.close(); //释放资源
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

java的Non-Blocking I/O编程

1 以上所讲的TCP和UDP编程性能比较差,他们是同步阻塞的,而java的NIO编程相对http与UDP有以下几个优点:
(1) 提供非阻塞的通讯等方式
(2) 避免同步I/O效率过于低
(3) 一个线程管理多个连接
(4) 减少线程多的压力
2 NIO编程的主要类在java.nio包中
–Buffer 缓存区
– Channel 通道
– Selector 多路选择器
3 NIO客户端服务端通讯示意图
在这里插入图片描述
(1) Buffer缓冲区,一个可以读写内存的缓冲区
–ByteBuffer,CharBuffer,DoubleBuffer,IntBuffer,LongBuffer
shortBuufer
(2) 四个主要属性
capacity 容量, position 读写位置
limit 界限, mark 标记,用于重复一个读/写操作
(3) Channel通道
1 全双工的,支持读/写(而Stream是单流向的)
2 支持异步读写
3 和Buffer配合,提高效率
4 ServerSocketChannel 服务器TCP Socket 接入通道,接收客户端
5SocketChannel TCP Socket通道,可支持阻塞/非阻塞通讯
6DatagramChannel UDP 通道
7FileChannel 文件通道
(4)Selector多路选择器
1 每隔一段时间,不断轮询注册在其上的Channel
2 如果有一个Channel有接入、读、写操作,就会被轮询出来
3 根据SelectionKey可以获取相应的Channel,进行后续IO操作
4 selectKey的四种类型
• OP_CONNECT
• OP_ACCEPT
• OP_READ
• OP_WRITE
server端

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NioServer {

    public static void main(String[] args) throws IOException {
    	int port = 8001;
    	Selector selector = null;
    	ServerSocketChannel servChannel = null;
    	
    	try {
    		//产生出一个selector的多路选择器
			selector = Selector.open();
			//建立全双工通道,建立好之后就可以进行连接
			servChannel = ServerSocketChannel.open();
			//配置为非阻塞模式
			servChannel.configureBlocking(false);
			//该服务的channel驻守在1024端口
			servChannel.socket().bind(new InetSocketAddress(port), 1024);
			//多路选择器绑定channel,他会监听此channel下的所有子channerl
			servChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("服务器在8001端口守候");
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
    	
    	while(true)
    	{
    		try {
    			//轮询所有的channel,看哪个channel有数据交换
    			selector.select(1000);
    			Set<SelectionKey> selectedKeys = selector.selectedKeys();
    			Iterator<SelectionKey> it = selectedKeys.iterator();
    			SelectionKey key = null;
    			while (it.hasNext()) {
    				key = it.next();
    				it.remove();
    				try {
    					//如果有 那么将这个key丢给handleInput进行处理
    					handleInput(selector,key);
    				} catch (Exception e) {
    					if (key != null) {
    						key.cancel();
    						if (key.channel() != null)
    							key.channel().close();
    					}
    				}
    			}
    		} 
    		catch(Exception ex)
    		{
    			ex.printStackTrace();    			
    		}
    		
    		try
    		{
    			Thread.sleep(500);
    		}
    		catch(Exception ex)
    		{
    			ex.printStackTrace();    			
    		}
    	}
    }
    
    public static void handleInput(Selector selector, SelectionKey key) throws IOException {

		if (key.isValid()) {
			// 处理新接入的请求消息
			if (key.isAcceptable()) {
				// Accept the new connection
				ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
				SocketChannel sc = ssc.accept();
				sc.configureBlocking(false);
				// Add the new connection to the selector
				sc.register(selector, SelectionKey.OP_READ);
			}
			//数据可以读的
			if (key.isReadable()) {
				// Read the data
				SocketChannel sc = (SocketChannel) key.channel();
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				int readBytes = sc.read(readBuffer);
				if (readBytes > 0) {
					readBuffer.flip();
					byte[] bytes = new byte[readBuffer.remaining()];
					readBuffer.get(bytes);
					String request = new String(bytes, "UTF-8"); //接收到的输入
					System.out.println("client said: " + request);
					
					String response = request + " 666";
					doWrite(sc, response);
				} else if (readBytes < 0) {
					// 对端链路关闭
					key.cancel();
					sc.close();
				} else
					; // 读到0字节,忽略
			}
		}
	}

	public static void doWrite(SocketChannel channel, String response) throws IOException {
		if (response != null && response.trim().length() > 0) {
			byte[] bytes = response.getBytes();
			ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
			writeBuffer.put(bytes);
			writeBuffer.flip();
			channel.write(writeBuffer);
		}
	}
}

cilent端

package nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

public class NioClient {

	public static void main(String[] args) {

		String host = "127.0.0.1";
		int port = 8001;

		Selector selector = null;
		SocketChannel socketChannel = null;

		try 
		{
			selector = Selector.open();
			socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false); // 非阻塞

			// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
			if (socketChannel.connect(new InetSocketAddress(host, port))) 
			{
				socketChannel.register(selector, SelectionKey.OP_READ);
				doWrite(socketChannel);
			} 
			else 
			{
				socketChannel.register(selector, SelectionKey.OP_CONNECT);
			}

		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}

		while (true) 
		{
			try 
			{
				selector.select(1000);
				Set<SelectionKey> selectedKeys = selector.selectedKeys();
				Iterator<SelectionKey> it = selectedKeys.iterator();
				SelectionKey key = null;
				while (it.hasNext()) 
				{
					key = it.next();
					it.remove();
					try 
					{
						//处理每一个channel
						handleInput(selector, key);
					} 
					catch (Exception e) {
						if (key != null) {
							key.cancel();
							if (key.channel() != null)
								key.channel().close();
						}
					}
				}
			} 
			catch (Exception e) 
			{
				e.printStackTrace();
			}
		}
	

		// 多路复用器关闭后,所有注册在上面的Channel资源都会被自动去注册并关闭
//		if (selector != null)
//			try {
//				selector.close();
//			} catch (IOException e) {
//				e.printStackTrace();
//			}
//
//		}
	}

	public static void doWrite(SocketChannel sc) throws IOException {
		//新建一个随机字符串
		byte[] str = UUID.randomUUID().toString().getBytes();
		//新建一个buffer缓冲区
		ByteBuffer writeBuffer = ByteBuffer.allocate(str.length);
		//将字符串放入buffer缓冲区
		writeBuffer.put(str);
		writeBuffer.flip();
		//将此缓冲区写入通道
		sc.write(writeBuffer);
	}

	public static void handleInput(Selector selector, SelectionKey key) throws Exception {

		if (key.isValid()) {
			// 判断是否连接成功
			SocketChannel sc = (SocketChannel) key.channel();
			if (key.isConnectable()) {
				if (sc.finishConnect()) {
					sc.register(selector, SelectionKey.OP_READ);					
				} 				
			}
			if (key.isReadable()) {
				ByteBuffer readBuffer = ByteBuffer.allocate(1024);
				int readBytes = sc.read(readBuffer);
				if (readBytes > 0) {
					readBuffer.flip();
					byte[] bytes = new byte[readBuffer.remaining()];
					readBuffer.get(bytes);
					String body = new String(bytes, "UTF-8");
					System.out.println("Server said : " + body);
				} else if (readBytes < 0) {
					// 对端链路关闭
					key.cancel();
					sc.close();
				} else
					; // 读到0字节,忽略
			}
			Thread.sleep(3000);
			doWrite(sc);
		}
	}
}

Netty编程

(1) 1 一个非阻塞的网络端,服务端开发框架
2 基于异步事件驱动模型
3 简化java的tcp和udp编程
4 支持http ssl多种协议
5 支持多种数据格式,如json等
(2) 关键技术
1 ServerSocketChannel/NIOServerSocketChannel
2 SocketChannel/NIOSocketChannel
(3) 事件驱动
1 为每个通道定义一个EventLoop,处理所有的IO事件
2 EventLoop注册事件
3 EventLoop将事件派发给ChannelHandler
4 EventLoop安排进一步操作
(4) 关键技术
1 事件
–事件按照数据流向进行分类
– 入站事件:连接激活/数据读取
– 打开远程连接/写数据
2 事件处理
– Channel通道发生数据或者改变
– EventLoop会将事件分类,并且调用ChannelHandler返回数据
3 我们需要实现ChannelHandler的回调函数
4 ChannelInboundHandler和ChannelnOutboundHandler,对应的是上面的入站事件和出战事件
5 ChannelHandler工作模式,责任链
责任链模式:
—将请求接收者连成一条链
—在链上传递请求,直到有一个接收者处理该请求
—避免请求者和接收者的耦合
6 byteBuf提供丰富的API操作
7 代码示例
(客户端)
```
package netty1;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

public class EchoClient {
    
	public static void main(String[] args) throws Exception {
    	String host = "localhost";
        int port = 8001;
        //注册事件
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                .channel(NioSocketChannel.class)
                .remoteAddress(new InetSocketAddress(host, port))
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch)
                        throws Exception {
                        ch.pipeline().addLast(new EchoClientHandler());
                    }
                });
            ChannelFuture f = b.connect().sync();
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }   
}
```
(2) 客户端的handler对象
package netty1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler
    extends SimpleChannelInboundHandler<ByteBuf> {
    //给服务端写入Netty rocks
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",
                CharsetUtil.UTF_8));
    }
  
  //接收服务端的回应
    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
        System.out.println(
                "Client received: " + in.toString(CharsetUtil.UTF_8));
    }
  //客户端异常捕捉
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

(3) 服务端

package netty1;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

public class EchoServer {
    public static void main(String[] args) throws Exception {
        int port = 8001;
        //后期要进行处理的handler对象
        final EchoServerHandler serverHandler = new EchoServerHandler();
        //定义一个EventLOOP事件组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
        	//ServerBootstrap是netty中的一个服务器引导类
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                .channel(NioServerSocketChannel.class)  //设置通道类型
                .localAddress(new InetSocketAddress(port))  //设置监听端口
                .childHandler(new ChannelInitializer<SocketChannel>() { //初始化责任链
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(serverHandler); //添加处理类,所有的事件处理,都送到serverHandler这个对象中处理
                    }
                });

            ChannelFuture f = b.bind().sync();  //开启监听
            if(f.isSuccess()){
            	System.out.println(EchoServer.class.getName() +
                        " started and listening for connections on " + f.channel().localAddress());
            }
            
            f.channel().closeFuture().sync();  //等待进程结束
        } finally {
            group.shutdownGracefully().sync();
        }
    }    
}

(4) 服务端handler对象

package netty1;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    //接收客户端请求并且回复客户端
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        String content = in.toString(CharsetUtil.UTF_8);
        System.out.println("Server received: " + content);
        
        ByteBuf out = ctx.alloc().buffer(1024);
        out.writeBytes((content + " 666").getBytes());
        ctx.write(out);
    }
     
     //写入客户端数据,并且监听服务端端口是否关闭
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {   	
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                .addListener(ChannelFutureListener.CLOSE);
    }
   	//	服务端异常捕捉
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
        Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

注册多个handler
client

package netty2;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
 
public class EchoClient {
 
    public static void main(String[] argsw) throws Exception {
    	int port = 8001;
        final EchoClientHandler clientHandler = new EchoClientHandler();
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(eventLoopGroup).
                    channel(NioSocketChannel.class).
                    remoteAddress(new InetSocketAddress("localhost",port)).
                    handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(clientHandler);
                        }
                    });
            
            //异步连接远程服务,连接远程服务成功后,输出"已经连接到服务器!"
            final ChannelFuture f = b.connect();
            f.addListener(new GenericFutureListener<Future<? super Void>>() {
                @Override
                public void operationComplete(Future<? super Void> future) throws Exception {
                    if (future.isSuccess()) {
                        System.out.println("已经连接到服务器!");
                        ByteBuf byteBuf = Unpooled.copiedBuffer("aaaaaaaaaaaaaaaa", Charset.defaultCharset());
                        ChannelFuture channelFuture = f.channel().writeAndFlush(byteBuf);
                    }else {
                        Throwable throwable = future.cause();
                        throwable.printStackTrace();
                    }
                }
            });
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            eventLoopGroup.shutdownGracefully().sync();
        }
    }
}

clinetHandler,负责clinet端接收服务端的响应

package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

/**
 * Listing 2.3 ChannelHandler for the client
 *
 * @author <a href="mailto:norman.maurer@gmail.com">Norman Maurer</a>
 */
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {


    //判断与服务器通道是否激活
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("和服务器通道已经激活");
    	//ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
    }

    //读取服务端传过来的信息
    @Override
    public void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) {
        ByteBuf in = msg;
        System.out.println("读取服务端="+in.toString(CharsetUtil.UTF_8));
    }
    //服务端异常捕捉
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端

package netty2;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
 
import java.net.InetSocketAddress;
 
public class EchoServer {
 
    public static void main(String[] args) throws Exception {
        //定义端口
    	int port = 8001;
    	//定义第 一个handler
    	final EchoServerFirstInHandler firstHandler = new EchoServerFirstInHandler();
    	// 定义第二个handler
    	final EchoServerSecondInHandler secondHandler = new EchoServerSecondInHandler();
    	//定义第三个handler
        final EchoServerOutHandler outHandler = new EchoServerOutHandler();
        //定义事件流
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //绑定多个handler,
            serverBootstrap.group(eventLoopGroup).
                    channel(NioServerSocketChannel.class).//指定channel使用NIO传输
                    localAddress(new InetSocketAddress(port)).//执行端口设置套接字地址
                    childHandler(new ChannelInitializer<SocketChannel>() {
                    	//Channel的channelpipeline
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //将三个handler分别加入channelPipeline ,分别处理客户端消息
                            ChannelPipeline channelPipeline = socketChannel.pipeline();
                            channelPipeline.addFirst(outHandler);
                            channelPipeline.addLast(firstHandler);
                            channelPipeline.addLast(secondHandler);
                        }
                    });
            ChannelFuture f = serverBootstrap.bind().sync();//异步绑定服务器,调用sync()方法阻塞等待直到绑定完成
            f.channel().closeFuture().sync();//获得Channel的closeFutrue,并且阻塞当前线程直到它完成
        } catch (InterruptedException e) {
            eventLoopGroup.shutdownGracefully().sync();
        }
    }
}

服务端handler1

package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
 
public class EchoServerFirstInHandler extends ChannelInboundHandlerAdapter {
 
 
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 注册事件");
        ctx.fireChannelRegistered();
    }
 
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 取消注册事件");
        ctx.fireChannelUnregistered();
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 有新客户端连接接入。。。"+ctx.channel().remoteAddress());
        ctx.fireChannelActive();
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: 失去连接");
        ctx.fireChannelInactive();
    }
 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("FirstHandler: 读客户端传入数据="+in.toString(CharsetUtil.UTF_8));
        final ByteBuf byteBuf = Unpooled.copiedBuffer("FirstHandler channelRead data!", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf);
        ctx.fireChannelRead(msg);
        //ReferenceCountUtil.release(msg);
    }
 
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx){
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {                    
                }else { 
                }
                
            }
        });
        final ByteBuf byteBuf = Unpooled.copiedBuffer("FirstHandler channelReadComplete data!", CharsetUtil.UTF_8);
        ctx.writeAndFlush(byteBuf).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess())  {
                	System.out.println("FirstHandler: 执行成功="+future.isSuccess());
                }else {
 
                }
                //ReferenceCountUtil.release(byteBuf);
            }
        });
        ctx.fireChannelReadComplete();
    }
 
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("FirstHandler userEventTriggered");
    }
 
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("FirstHandler: channelWritabilityChanged");
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端handler2

package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

 
import java.util.concurrent.ScheduledExecutorService;
 
public class EchoServerSecondInHandler extends ChannelInboundHandlerAdapter {
 
 
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 注册事件");
    }
 
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 取消注册事件");
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 有新客户端连接接入。。。"+ctx.channel().remoteAddress());
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler: 失去连接");
    }
 
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf in = (ByteBuf) msg;
        System.out.println("SecondHandler: 读客户端传入数据="+in.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(Unpooled.copiedBuffer("SecondHandler channelRead data!", CharsetUtil.UTF_8));
        //ctx.fireChannelActive();
    }
 
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx){
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess()) {                    
                }else { 
                }
            }
        });
        ctx.writeAndFlush(Unpooled.copiedBuffer("SecondHandler: channelReadComplete data!", CharsetUtil.UTF_8)).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                if (future.isSuccess())  {
                	System.out.println("SecondHandler: 执行成功="+future.isSuccess());
                }else {
 
                }
            }
        });
    }
 
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("SecondHandler userEventTriggered");
    }
 
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("SecondHandler channelWritabilityChanged");
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}

服务端处理完成一次客户端请求,都会执行一次outHandler输出

package netty2;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
 
import java.nio.charset.Charset;
 
public class EchoServerOutHandler extends ChannelOutboundHandlerAdapter {
 
    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        System.out.println("EchoServerOutHandler write: "
           +((ByteBuf)msg).toString(Charset.defaultCharset()));
        ctx.write(msg, promise);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值