Java学习笔记(49)——网络

这里将自己学习java及其应用的一些笔记、积累分享一下,如果涉及到了文章、文字侵权,请联系我删除或调整。


一、网络

1.1 概述

  • 查询本级网络基本情况

win + r,输入 cmd

输入命令:

ipconfig

  • 尝试连接本局域网内的其他IP

ping 192.168.4.xxx

如果ping不通,关闭防火墙(注意在公开网络环境下,存在隐患)

1.2 Socket网络通信

  • Socket 网络套接字
  • 主机之间,需要能用 ip 地址,找到对方
  • 在主机上选择一个端口,来插插头(Socket

1.3 服务器端

  • 在一个端口上启动服务
  • 被动地等待客户端来发起连接

​​​​​​​1.4 客户端

  • 向指定服务器的 ip 地址和端口号,发起连接
  • 建立连接通道
  • 连接通道的两端,都有一个插头

​​​​​​​​​​​​​​​​​​​​​1.5 ServerSocket类

  • 启动服务,ServerSocket ss = new ServerSocket(8000);

端口必须独占

01024,是常见服务的默认端口

50000之后,系统保留端口用来动态分配

  • 方法

accept()方法:

暂停,等待客户端发起连接;

建立与客户端的连接通道,并返回通道的服务器端插头对象。

close()方法:

停止服务,释放端口。

1.6 网络通信中的阻塞操作

  • ServerSocketObject.accept()

阻塞,暂停等待客户端发起连接

  • in.read(),对于Socket对象、ServerSocket对象均可获取InputStream对象

阻塞,接收数据,对方不发送,暂停死等数据

1.7 练习:服务器、客户端小案例

package 网络;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class Sever_服务器测试 {
	public static void main(String[] args) throws IOException {
		// 9000端口上启动服务
		ServerSocket ss = new ServerSocket(9000);
		System.out.println("服务器已经在9000端口上启动服务!");
		// 等待客户可发起连接
		// 建立连接后,返回插头对象
		Socket s = ss.accept();
		System.out.println("客户端已连接");
		// 从插头对象中取出“火线”,“零线”即输入流和输出流
		InputStream in = s.getInputStream();
		OutputStream out = s.getOutputStream();
		
		/*
		 *	通信协议:通信流程、数据格式
		 *	第一步:接收“hello”;
		 *	第二部:发送“world”
		 *	数据格式为英文		 
		 */
		// 接收
		for(int i = 0;i<5;i++) {
			char c = (char) in.read();
			System.out.print(c);
		}
		// 发送
		out.write("world".getBytes());//getBytes()无参,默认将字符串转为系统默认编码方式
		out.flush();// .flush()刷出本地内存中的缓存数据
		
		
		s.close();// 完成数据交换,断开和客户端的连接“拔插头”
		ss.close();// 停止服务,释放断口
	}
}
package 网络;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Clinet_客户端 {
	public static void main(String[] args) throws UnknownHostException, IOException {
		// 向指定服务器的指定断口发起连接,IP地址请根据自己测试的环境修改
		Socket s = new Socket("172.218.1.74", 9000);
		
		// 从插头对象中取出火线,零线即输入流和输出流
		InputStream in = s.getInputStream();
		OutputStream out = s.getOutputStream();

		/*
		 * 	通信协议:通信流程、数据格式 
		 * 	第一步:发送“hello”; 
		 * 	第二部:接收“world” 数据格式为英文
		 */
		// 发送数据
		out.write("hello".getBytes());
		out.flush();
		// 接收数据
		for(int i = 0;i<5;i++) {
			char c = (char) in.read();
			System.out.print(c);
		}
		s.close();
	}	
}

1.8 服务器端的线程模型

1.8.1 练习:回声机

通过这个案例,我们尝试练习、熟悉一下服务器端的线程模型。需要说明的是,这只是一个用于熟悉相关知识点小案例,其中的异常处理还不完善,我们暂时忽略了这部分的内容。

package 回声服务器;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/*	
 * 	支持多客户端连接
 * 	回声服务:服务器接收客户端数据,再原封不动,发回给客户端
 */
public class EchoServer{
	// 启动服务的方法
	public void launch() {// launch 启动、点火、发射
		// 新建匿名类对象并调用start方法
		new Thread() {
			@Override
			public void run() {
				try {
					//9000断口启动服务
					ServerSocket ss = new ServerSocket(9000);
					System.out.println("服务器已在9000断口启动服务!");
					// 死循环,主机不停的接收新的连接请求并转给“通信”线程
					while(true) {
						Socket s = ss.accept();
						System.out.println("一个客户端已上线!");
						// 新创一个通信线程,将客户端转交给通信线程完成客户数据交流服务
						// 主服务器循环至下一轮接收新的客户连接申请
						TongXinThread t = new TongXinThread(s);
						t.start();
					}
				} catch (Exception e) {
					System.out.println("服务无法在9000客户端启动,或者服务异常停止!");
				}
			}
		}.start();
	}

	// 内部类:通信线程
	class TongXinThread extends Thread{
		private Socket s;
		// 获得主服务器传来的Socket对象,利用输入输出流通信
		public TongXinThread(Socket socked) {
			this.s = socked;
		}

		@Override
		public void run() {// 涉及到数据流收发数据就有异常
			try {
				/*	通信协议
				 * 		流程:while(true){收,发};
				 * 		数据格式:字符串支持中文UTF-8,
				 * 				每一行文本以换行符结尾;
				 * 	s.getInputStream 网络输入/输出流
				 * 	InputStreamReader 输入/输出编码转换流
				 * 	BufferedReader 使用readLine()方法
				 */
				BufferedReader in = new BufferedReader(// readLine
								new InputStreamReader(// 字符串编码转化
								s.getInputStream(),"UTF-8"));// 获取输入流
				PrintWriter out = new PrintWriter(
						new OutputStreamWriter(
						s.getOutputStream(),"UTF-8"));
				String line;
				
				while((line = in.readLine())!= null) {// readLine可能抛异常
//					System.out.println(line); 显示客户端发送的消息
					out.println(line);// 必须有ln,以换行符分割
					out.flush();
				}
				// TODO 网络正常断开,是null值,退出循环
				
			} catch (Exception e) {
				// TODO 网络异常断开
			}
			// 正常断开与异常断开统一处理
			System.out.println("一个客户端断开!");
		}
	}
	
	public static void main(String[] args) {
		EchoServer s = new EchoServer();
		s.launch();
	}
}
package 回声服务器;

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.util.Scanner;


public class EchoClinet {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("172.218.1.74",9000);
		
		BufferedReader in = new BufferedReader(
				new InputStreamReader(
				s.getInputStream(),"UTF-8"));
		PrintWriter out = new PrintWriter(
				new OutputStreamWriter(
				s.getOutputStream(),"UTF-8"));
		
		while(true) {
			System.out.println("请输入:");
			String msg = new Scanner(System.in).nextLine();
			
			out.println("用户输入:"+msg);
			out.flush();
			
			String r = in.readLine();
			System.out.println("回声:"+r);
		}	
	}
}

1.8.2 练习:Boss直聊

在上一个练习中,我们通过“回声”服务,基本了解了通信线程模型。在此,我们计划把这个模型再扩展一下,将其丰富为一个支持多人聊天的“群聊室”,这其中也会用到很多之前java学习中学到的内容。算是一个综合练习吧,毕竟温故而知新嘛 :),案例的详细细节,请大家看注释吧,在正文里就不再赘述了。

package Boss直聊;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class ChatServer {
	// 集合收集每一个链接的对象,通过遍历,群发聊天信息
	private ArrayList<TongXinThread> list = new ArrayList<ChatServer.TongXinThread>();
	
	public void launch() {// launch 启动、点火、发射
		// 新建匿名类对象并调用start方法
		new Thread() {
			@Override
			public void run() {
				try {
					//9000断口启动服务
					ServerSocket ss = new ServerSocket(9000);
					System.out.println("服务器已在9000断口启动服务!");
					// 死循环,主机不停的接收新的连接请求并转给“通信”线程
					while(true) {
						Socket s = ss.accept();
						System.out.println("一个客户端已上线!");
						// 新创一个通信线程,将客户端转交给通信线程完成客户数据交流服务
						// 主服务器循环至下一轮接收新的客户连接申请
						TongXinThread t = new TongXinThread(s);
						t.start();
					}
				} catch (Exception e) {
					System.out.println("服务无法在9000客户端启动,或者服务异常停止!");
				}
			}
		}.start();
	}
	
	// 通信线程
	class TongXinThread extends Thread{
		private Socket s;
		private BufferedReader in;
		private PrintWriter out;
		private String name;
		
		public TongXinThread(Socket socket) {
			this.s = socket;
		}
		// 私聊
		public void send(String msg) {
			out.println(msg);
			out.flush();
		}
		// 群聊
		public void sendAll(String msg) {
			for(TongXinThread t : list) {
				t.send(msg);
			}
		}
		
		@Override
		public void run() {
			try {
				this.in = new BufferedReader(// readLine
						new InputStreamReader(// 字符串编码转化
						s.getInputStream(),"UTF-8"));// 获取输入流
				this.out = new PrintWriter(
						new OutputStreamWriter(
						s.getOutputStream(),"UTF-8"));
				
				// 1接收客户端昵称
				this.name = in.readLine();
				// 2给当前客户端发送“欢迎”
				send(this.name+"欢迎你进入激情聊天室!");
				// 3把当前通信对象加入list
				list.add(this);// 进入“群”,则可以接收到群聊信息
				// 4群发客户上线消息
				sendAll(this.name+"已经进入了聊天室!");
				
				// 群聊,广播客户发送的消息
				String line;
				while((line =in.readLine())!=null) {
					sendAll(this.name+"说:");
				}
				//  TODO 用户(网络)正常断开,是null值,退出循环
			} catch (Exception e) {
				// TODO 用户(网络)异常断开
			}
			// 统一处理异常
			// 断开连接:将当前对象移除list
			list.remove(this);
			sendAll(this.name+"离开了聊天室");
		}
	}
	
	public static void main(String[] args) {
		ChatServer s = new ChatServer();
		s.launch();
	}
}
package Boss直聊;

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

public class ChatClient {
	private Socket s;
	private BufferedReader in;
	private PrintWriter out;
	private String name;

	public void launch() {
		try {
            // 博友在测试时,请注意更换为自己ChatSever的IP和端口
			this.s = new Socket("172.218.1.74", 9000);
			this.in = new BufferedReader(// readLine
					new InputStreamReader(// 字符串编码转化
					s.getInputStream(), "UTF-8"));// 获取输入流
			this.out = new PrintWriter(
					new OutputStreamWriter(
					s.getOutputStream(), "UTF-8"));

			// 起个昵称并发送至服务器
			System.out.println("昵称:");
			this.name = new Scanner(System.in).nextLine();
			out.println(this.name);
			out.flush();
			// 因为接收信息与循环读入用户输入信息是阻塞的,所以用两个线程并行执行
			// 接收线程(匿名内部类),接收聊天室的信息
			new Thread() {
				@Override
				public void run() {
					receive();
				}
			}.start();

			// 输入线程,用户输入欲发送的信息
			new Thread() {
				@Override
				public void run() {
					input();
				}
			}.start();

		} catch (Exception e) {
			System.out.println("无法链接服务器!");
		}
	}

	protected void input() {
		while(true) {
			System.out.println("输入聊天内容=>");
			String s = new Scanner(System.in).nextLine();
			out.println(s);// 必须加ln
			out.flush();
		}
	}

	protected void receive() {
		// 接收数据有异常
		try {
			String line;
			while((line = in.readLine())!=null) {
				System.out.println(line);
			}
			//  TODO 用户(网络)正常断开,是null值,退出循环
		} catch (Exception e) {
			// TODO 用户(网络)异常断开
		}
		// 统一处理异常,断开连接
		System.out.println("已经与服务器断开连接!");
	}

	public static void main(String[] args) {
		ChatClient c = new ChatClient();
		c.launch();
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值