IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 11.网络编程 -> 正文阅读

[网络协议]11.网络编程

1.TCP通信

1.1 Socket原理

Socket简介
Socket通常称作套接字用于描述IP地址和端口,是一个通信连接的句柄。
在Internet上的主机一般运行了多个服务软件,同时提供集中服务。
每种服务都打开一个Soket,并绑定一个端口上,不同的端口对应于不同的服务。
应用程序通常通过“套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类位于java.net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会生成一个Socket实例,操作这个实例,完成所需要的会话。

1.2 获取本地地址和端口号

Java.net.Socket为套接字类,其提供了很多方法, 其中我们可以通过Socket获取本地的地址以及端口号。
Int getLocalPort();
该方法用于获取本地使用的端口号
InetAddress getLocalAddresss();
该方法用于获取套接字绑定的本地地址
使用InetAddress获取本地的地址方法:
String getCanonicalHostName();
获取此IP地址的完全限定域名。
String getHostAddress()
返回IP地址字符串(以文本表现形式)

public static void testSocket() throws Exception {
	Socket socket =  new Socket("127.0.0.1",8088);
	InetAddress add = socket.getLocalAddress();
	System.out.println(add.getCanonicalHostName());
	System.out.println(add.getHostAddress());
	System.out.println(socket.getLocalPort());
}

1.3 获取远端地址和端口号

通过Socket获取远端的地址以及端口号。
Int getPort();
该方法用于获取远端使用的端口号
InetAddress.getInetAddress();
该方法用于获取套接字绑定的远端地址

public static void testSocket() throws Exception {
	Socket socket =  new Socket("localhost",8088);
	InetAddress inetAdd = socket.getLocalAddress();
	System.out.println(inetAdd.getCanonicalHostName());
	System.out.println(inetAdd.getHostAddress());
	System.out.println(socket.getPort());
}

1.4.获取网络输入流和网络输出流

通过Socket获取输入流于输出流,这两个方法是使用Socket通讯的关键方法。

inputStream getInputStream()
该方法用于返回此套接字的输入流。
OutputStream.getOutputStream()
该方法用于返回此套接字的输出流。

public static void testSocket() throws Exception {
	Socket socket =  new Socket("localhost",8088);
	InputStream  in = socket.getInputStream();
	OutputStream out = socket.getOutputStream();
}

1.5 close方法

当使用Socket进行通讯完毕后,要关闭Socket已释放系统资源。
Void close();
关闭此套接字。当关闭了该套接字后也会同时关闭由此获取的输入流与输出流。

1.6 Server端ServerScoket监听

Java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口,之后监听Socket的连接:

//创建ServerSocket并申请服务端口8088
ServerSocket server = new ServerSocket(8088);
/*
 * 方法会产生阻塞,直到某个Socket连接,并返回请求连接的Socket
 */
Socket socket = server.accept();

1.7 Client端Socket连接

当服务端创建ServerSocket并通过accept()方法侦听后,我们就可以通过客户端应用程序中创建Socket来向服务端发起连接。
需要注意的是,创建Socket的同时就发起连接,若连接异常会抛出异常。

//参数1:服务端的IP地址,参数2:服务端的服务端口
ServerSocket server = new ServerSocket(8088);

1.8 C-S端通信模型

在这里插入图片描述

1.9. 聊天室案例

使用Socket于ServerSocket建立客户端于服务端的连接并使用客户端向服务端发送一条消息

改善代码,使用户可以重复通过在控制台输入内容,并将内容发送给服务端。

Server.java

package day05.ZHONG;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
	private static BufferedReader br=null;
	private static PrintWriter pw=null;
	private static ServerSocket ss;
	private static Socket s;
	static Scanner scanner=new Scanner(System.in);
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			ss=new ServerSocket(5500);
			System.out.println("服务器正常启动。。。。");
		    s=ss.accept();//阻塞方法
		    System.out.println("连接成功"+s.getRemoteSocketAddress());
		    br=new BufferedReader(new InputStreamReader(s.getInputStream()));
			pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
			while(true){
				String string=br.readLine();
				System.out.println("Server读到:"+string);
				System.out.println("Server端请输入:");
				String str=scanner.next();
				pw.println(str);
				pw.flush();
				if(str.equals("exit")){
					break;
				}
			}
		} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
		}
		try {
			pw.close();
			br.close();
		} catch (IOException e1) {
				// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}

}

Client

package day05.ZHONG;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
	private static PrintWriter pw=null;
	private static BufferedReader br=null;
	private static Socket s;
	static Scanner scanner=new Scanner(System.in);
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			Socket s=new Socket(InetAddress.getLocalHost(),5500);
			pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
			br=new BufferedReader(new InputStreamReader(s.getInputStream()));
			while(true){
				System.out.println("Client端请输入:");
				String str = scanner.next();
				pw.println(str);
				pw.flush();
				String string=br.readLine();
				System.out.println("Client读到:"+string);
				if(str.equals("exit")){
					break;
				}
			} 
		}catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
		}
		try {
			br.close();
			pw.close();
		} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
		}
	}
}

1.10 Server端多线程模型

在这里插入图片描述

如果向是一个服务端可以支持多客户端连接,我们需要了解以下问题:
循环调用accept方法侦听客户端的连接
使用线程来处理单一客户端的数据交互

因为需要处理多客户端,所以服务端要周期性循环调用accept方法,但该方法会产生阻塞,所以与某个客户端的交互就需要使用线程来并发处理。

重构聊天室案例,使用线程来实现一个客户端可以同时接受多个客户端的信息。

1.10.1 Server.java

package day05.ZHONG.duo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Server {

	public static void main(String[] args) throws IOException, InterruptedException {
		// TODO Auto-generated method stub
		ServerSocket ss = new ServerSocket(10086);
		System.out.println("服务器正常启动。。。");
		while(true){
			Socket socket = ss.accept();
			System.out.println("用户接入成功。。。");
			/*
			 * 每次连接成功一个客户端,则启动一个线程为其服务
			 */
			new ServerThread(socket).start();
		}		
	}
}

1.10.2 Client.java

package day05.ZHONG.duo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {

		Socket socket = new Socket("127.0.0.1",10086);
		PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
		BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		Scanner scanner = new Scanner(System.in);
		while(true){
			String arg = scanner.nextLine();
			if("exit".equals(arg.trim())){
				pw.println("exit");
				pw.flush();
				break;
			}
			pw.println(arg);
			pw.flush();
			String str = br.readLine();
			System.out.println("Client read = "+str);
		}
//		Thread.sleep(300);
		pw.close();
		br.close();
	}
}

1.10.3 ServerThread.java


package day05.ZHONG.duo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

public class ServerThread extends Thread {

	private Socket socket;
	private InetAddress ip;
	public ServerThread(Socket socket) {
		ip =socket.getInetAddress();
		this.socket = socket;
	}

	@Override
	public void run() {
		BufferedReader br = null;
		PrintWriter pw = null;
		try {
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
			while (true) {
				String string = br.readLine();
				System.out.println("Server read  = 服务端"+ip+"说:" + string);
				System.out.println("Server端请输入: ");
				Scanner scanner = new Scanner(System.in);
				String str = scanner.nextLine();//服务端说
				
				pw.println( "【"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) 
						+ "】服务端说:"+str);
				pw.flush();
				if ("exit".equals(str)) {
					break;
				}
			}

			// Thread.sleep(300);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally{
			try {
				br.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				pw.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

1.11聊天室群发

重构聊天室案例,使服务器可以将用户的信息转发给所有客户端。并在每个客户端控制台上显示。

Server.java

package day05;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * 聊天室服务端
 * @author Administrator
 */
public class Server {
	/*
	 * 运行在服务端的ServerSocket
	 * 用于监听服务端口,并接收客户端的链接
	 */
	private ServerSocket server;
	/*
	 * 该集合用来保存所有客户端的输出流,便于广播
	 */
	private List<PrintWriter> allOut;
	/**
	 * 构造方法,用来初始化属性
	 */
	public Server(){
		try {
			allOut = new ArrayList<PrintWriter>();
			/*
			 * 创建ServerSocket的同时需要指定
			 * 开启的服务端口。
			 * 端口:每种通讯协议都会有端口。
			 * 作用:当客户端通过ip地址链接到当前计算机
			 * 后,就是通过端口找到我们的应用程序的。
			 * 因为在计算机中有很多程序都要使用网络,
			 * 都会用到TCP这样的协议,那么远程计算机
			 * 发送过来的数据交给那个程序就是靠端口。
			 * 
			 */
			server = new ServerSocket(8088);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 向共享集合中添加一个输出流
	 * @param out
	 */
	private synchronized void addOut(PrintWriter out){
		allOut.add(out);
	}
	/**
	 * 将给定的输出流从共享集合中删除
	 * @param out
	 */
	private synchronized void removeOut(PrintWriter out){
		allOut.remove(out);
	}
	/**
	 * 将给定的消息发送给所有客户端
	 * @param message
	 */
	private synchronized void sendMessageToAllClient(String message){
		for(PrintWriter out : allOut){
			out.println(message);
		}
	}
	
	/**
	 * 服务端开始工作的方法
	 */
	public void start(){
		try {
			/*
			 * Socket accept()
			 * 该方法是一个阻塞方法,用于监听8088端口
			 * 直到一个客户端进行了链接,才会将于该客户端
			 * 交互的Socket返回。
			 */
			System.out.println("等待客户端链接...");
			Socket socket = server.accept();
			System.out.println("一个客户端链接了!");
			/*
			 * 启动一个线程,用来与当前客户端进行交互。
			 */			
			ClientHandler handler
				= new ClientHandler(socket);
			Thread t = new Thread(handler);
			t.start();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
	
	/**
	 * 这是一个线程要执行的任务,用于使得服务端可以
	 * 处理与某个客户端的交互工作。
	 * @author Administrator
	 *
	 */
	private class ClientHandler implements Runnable{
		/*
		 * 当前线程需要与这个客户端进行交互
		 */
		private Socket socket;
		
		//当前客户端的地址
		private String host;
		/**
		 * 创建用于与给定客户端进行交互的线程任务
		 * @param socket 当前线程需要交互的客户端Socket
		 */
		public ClientHandler(Socket socket){
			this.socket = socket;
			/*
			 * 通过socket可以获取远程计算机信息
			 */
			//获取远程计算机地址信息
			InetAddress address
				= socket.getInetAddress();
			//获取ip
			host = address.getHostAddress();
			System.out.println("["+host+"]上线了!");
			
		}
		
		public void run(){
			PrintWriter pw = null;
			try {
				/*
				 * 通过客户端的Socket获取输出流,用来将消息
				 * 发送给当前客户端
				 */
				OutputStream out 
					= socket.getOutputStream();
				
				OutputStreamWriter osw
					= new OutputStreamWriter(out,"UTF-8");
				
				pw = new PrintWriter(osw,true);
				//将这个客户端的输出流存入共享集合
				addOut(pw);
				
				
				/*
				 * InputStream getInputStream()
				 * Socket的该方法用来获取一个可以读取远端
				 * 计算机发送过来数据的输入流
				 */
				InputStream in = socket.getInputStream();
				InputStreamReader isr
					= new InputStreamReader(in,"UTF-8");
				BufferedReader br
					= new BufferedReader(isr);
				
				String message = null;
				/*
				 * 我们通过br.readLine()读取客户端发送过来的
				 * 一行字符串。这里该方法的执行由于客户端的操作
				 * 系统不同,这里的结果会有差异。
				 * 
				 * windows的客户端与服务端断开连接后:
				 * 这里的readLine方法会直接抛出异常,告知我们客户端
				 * 与我们断开了连接。
				 * 
				 * linux的客户端与服务端断开连接后:
				 * 这里的readLine方法会返回null。
				 */
				while((message = br.readLine())!=null){
					System.out.println("["+host+"]说:"+message);
//					pw.println(message);
					/*
					 * 当读取到该客户端发送过来的一句话后,转发
					 * 给所有客户端
					 */
					sendMessageToAllClient(
						"["+host+"]说:"+message);
				}
				
			} catch (Exception e) {
				
			} finally{
				//将该客户端的输出流从共享集合中删除
				removeOut(pw);
				/*
				 * 无论是什么操作系统的客户端与服务端断开
				 * 连接,这里都应当将该客户端的Socket关闭
				 * 以释放资源
				 */
				try {
					socket.close();
					System.out.println("["+host+"]下线了!");
				} catch (Exception e2) {
					
				}
			}
		}
	}
}

Client.java

package day05;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 聊天室客户端
 * @author Administrator
 *
 */
public class Client {
	/*
	 * 客户端的Socket,用于链接服务端的ServerSocket
	 * 并与服务端通讯
	 */
	private Socket socket;
	/**
	 * 构造方法,用来初始化客户端
	 */
	public Client(){
		try {
			/*
			 * 初始化Socket常使用带两个参数的构造方法
			 * 参数1:服务端的地址
			 * 参数2:服务端开启的服务端口
			 * 需要注意的是:创建的过程就是链接的过程,
			 * 若链接失败这里会抛出异常。
			 */
			socket = new Socket("localhost",8088);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/**
	 * 客户端开始工作的方法
	 */
	public void start(){
		try {
			/*
			 * 启动用来读取服务端发送过来的消息的
			 * 线程
			 */
			GetServerMessageHandler handler = new GetServerMessageHandler();
			Thread t = new Thread(handler);
			t.start();
			/*
			 * 若想将消息发送至服务端,我们需要通过
			 * socket获取输出流。
			 * 
			 * OutputStream getOutputStream()
			 * Socket提供了该方法用来获取一个字节输出流
			 * 便于我们将数据发送至远端计算机。
			 */
			OutputStream out = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
			PrintWriter pw= new PrintWriter(osw,true);
			
			//获取用户输入
			Scanner scanner = new Scanner(System.in);
			//循环将键盘输入的内容发送给服务端
			while(true){
				pw.println(scanner.nextLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
	/**
	 * 该线程用来接收服务端发送过来的消息,并输出到
	 * 客户端的控制台
	 * @author Administrator
	 *
	 */
	private class GetServerMessageHandler 
						implements Runnable{
		public void run(){
			try {
				InputStream in= socket.getInputStream();
				InputStreamReader isr = new InputStreamReader(in,"UTF-8");
				BufferedReader br = new BufferedReader(isr);
				String message = null;
				while((message = br.readLine())!=null){
					System.out.println(message);
				}
			} catch (Exception e) {
				
			}
		}
	}	
}

2.UDP通信

2.1构建接收包

DatagramPacket:UDP数据包基于IP建立的,每台主机有65535个端口可以使用。
构建接收包
DatatgramPacket(byte[] buf,int length)
将数据包中length长度的数据装进buf数组。

DatagramPacket(byte[] buf, int offset, int length)
将数据包中从offset开始、length长度的数据装进buf数组。

2.2 构建接收包

构造发送包
DatagramPacket(byte[] buf , int length , InetAddress clientAdress,int clientPort)
从buf数组中取出length长度的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-01 18:03:38  更:2021-12-01 18:06:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:36:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码