CoreJava学习11——网络编程

Java网络编程

1、Socket编程

Socket(套接字):封装着如端口号,ip地址,计算机名等信息的类。通过Socket我们可以和远程计算机通信。

网络通信模型

C/S Client/Server

客户端通航运行在用户的计算机上,客户端是一个特有的程序,实现特有的功能,连接服务器进行通信,谁发起连接谁是用户。

服务器端通常是等待客户端连接,提供功能服务并与之通信。

B/S

固定了客户端和通信协议和C/S结构。

通信协议:计算机通信的实质就是相互收发字节。那么按照一定的格式收发字节就是通信协议。

/*** 创建客户端Socket* Socket客户端类* 构造的时候就会根据给定的服务端ip地址和服务端的端口号尝试连接*/try {System.out.println(“开始连接”);Socket socket = new Socket(“172.16.3.33”, 8088);System.out.println(“与服务端连接成功!”);/*** 通过socket可以获取一组与服务器通信的输入输出流* 我们对其包装就可以方便进行读写信息了。* 通过socket拿到的是两个低级流(字节流)*/InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();/*** 向服务器发送字符,将字符输出流转换成缓冲字符输出流*/PrintWriter pw = new PrintWriter(out);pw.println(“你好服务器!”);pw.flush();/*** 收取服务器发送的字符串,包装为缓冲字符输入流*/BufferedReader reader = new BufferedReader(new InputStreamReader(in));//读取服务器发回的信息String info = reader.readLine();System.out.println(“服务端:”+info);} catch (IOException e) {//e.printStackTrace();}/*** 服务端* 服务器端打开socket等待客户端的连接* 服务器端的Socket名称是ServerSocket* 创建ServerSocket需要指定服务端口号*/public static void main(String[] args) {try {System.out.println(“dddd”);ServerSocket server = new ServerSocket(8050);/*** 监听端口* 等待客户端连接* 等待客户端连接的方法accept()* 该方法是一个阻塞方法,知道有客户端连接上该方法才会返回,返回的就是当前客户端的套接字。* 从中我们可以知道客户端的ip等信息。** accept()方法可以重复调用*/System.out.println(“启动完毕,等待连接”);Socket client = server.accept();System.out.println(“有一个客户端和我连接了”);/*** 通过socket获取输入流读取客户端的信息*/InputStream in = client.getInputStream();OutputStream out = client.getOutputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(in));System.out.println(“客户端:”+reader.readLine());//向客户端发信息PrintWriter pw = new PrintWriter(out);pw.println(“你好客户端”);pw.flush();} catch (IOException e) {//e.printStackTrace();}}

2、多线程Socket

Server端多线程:

服务器端无限循环接受客户端的访问,每连接都能茶圣一对新的Socket的实例。

为每个客户端连接创建一个独立线程,处理客户请求。

public static void main(String[] args) {try {ServerSocket server = new ServerSocket(8088);System.out.println(“启动完毕,等待连接”);while (true) {Socket client = server.accept();if(client==null)continue;System.out.println(“与客户端连接成功”+client.getInetAddress().getHostAddress());Handler handler = new Handler(client);//交给线程去处理Thread t = new Thread(handler);t.start();}} catch (IOException e) {}}/*** 与客户端通信线程* 负责与某个特定的socket的客户端进行通信* 每个线程实例负责一个客户端的通信*/private static class Handler implements Runnable {/*** 当前线程要通信的客户端socket*/private Socket client;public Handler(Socket client) {this.client = client;}public void run(){try {/** 通过socket获取客户端信息 */InputStream in = this.client.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(in));/** 死循环读取客户端信息 */while (true) {if (reader.readLine() == null)return;System.out.println(“客户端”+ this.client.getInetAddress().getHostAddress() + “:” + reader.readLine());}} catch (Exception e) {}}}

3、线程池

上面这种频繁的创建线程和销毁线程是非常消耗资源和性能的。

可以创建一些空的线程,将它们保存起来,当有任务需要并发执行时,我们取出一个空的线程来运行这个任务,当任务运行完毕后,在将线程收回,等待下次分配任务。

这样自始自终我们都只使用了开始创建的那些线程,并可以重复利用来节省性能开销。

JDK提供了线程池的管理器ExecutorService

通过Executors类的静态方法创建几个不同实现的线程池。

Executors.newCachedThreadPool();创建一个缓存线程池,当有任务的时候会检查线程池中是否有空线程,若有就使用它,若没有就创建新的线程。若长久没有使用的线程会自动回收。

Executors.newFixedThreadPool(int threads);创建一个可重用的,具有固定线程数的线程池。

Executors.newScheduledExecutor();创建只有一条线程的线程池,它可以在指定延迟后执行线程任务。

Executors.newSingleThreadExecutor();创建一个只有单线程的线程池,相当于Exceutors.newFixedThreadPool方法时传入参数1。里边维护着一个任务队列。

/*** 创建一个线程池,具有50个*/ExecutorService threadPool =Executors.newFixedThreadPool(50);try {ServerSocket server = new ServerSocket(8088);System.out.println(“启动完毕,等待连接”);/** 将转发消息线程启动 */SendMessageHandler sHandler = new SendMessageHandler();Thread sendTh = new Thread(sHandler);sendTh.start();while (true) {Socket client = server.accept();System.out.println(“与客户端连接成功”+client.getInetAddress().getHostAddress());Handler handler = new Handler(client);//交给线程去处理/*** 将需要并发的任务(Runnable)交给线程池* 让其分配空线程去运行该任务* 若线程都在工作,那么登载,直到有空线程为止。*/threadPool.execute(handler);//Thread t = new Thread(handler);//t.start();}} catch (IOException e) {}

4、双端队列

内部由两个队列实现,他们交替进行存取工作。这样至少可以保证2个线程同时进行存取操作。但是对于两个线程同时进行存或者取,还是要求同步的。

但是比单队列实现线程安全还是要快的。

BlockingDeque双端队列

实现:

1)ArrayBlockingDeque该类实现的构造方法要求我们传入一个整数,代表当前队列的长度。所以这个是一个固定大小的双端队列,存取原则为FIFO先入先出的原则。

当元素调用offer存入了队列时,若队列已满,那么可以设置延时等待,当超过了延时等待会返回存入失败。

2)LinkedBlockingDeque变长双端队列。长度不定,随着元素数量而增加,最大可以达到Integer.MAX_VALUE,重载构造方法可以传入一个整数,使之变为一个定长的队列。

3)PriorityBlockingDeque 这个和LinkedBlockingDeque相似,只不过是进去了自然排序后获取。

4)SynchronousQueue特殊的双端队列 存取步骤有要求,必须存一次取一次,交替进行。

例:

做事的能力往往只能给你一种机会,

CoreJava学习11——网络编程

相关文章:

你感兴趣的文章:

标签云: