带线程池的socket客户端与服务端

前言  

socket(套接字),Socket和ServerSocket位于java.net包中,之前虽然对socket有过一些了解,但一直都是云里雾里的,特意仔细的学习了一个socket,用socket模拟一个天气查询的功能,并且解决了几个使用socket过程中比较严重的问题。

最简单的客户端和服务端

服务端代码

 1 package cn.hucc.socket.server;
 2
 3 import java.io.DataInputStream;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.net.ServerSocket;
 7 import java.net.Socket;
 8
 9 /**
10  *
11  * @auth hucc
12  * @date 2015年10月10日
13  */
14 public class WeatherServer {
15
16     private static final int PORT = 8888;
17
18     public static void main(String[] args) {
19
20         ServerSocket server = null;
21         Socket socket = null;
22         DataInputStream dataInputStream = null;
23         DataOutputStream dataOutputStream = null;
24         try {
25             server = new ServerSocket(PORT);
26             System.out.println("天气服务端已经移动,监听端口:" + PORT);
27             socket = server.accept();
28
29             // 接受客户端请求
30             dataInputStream = new DataInputStream(socket.getInputStream());
31             String request = dataInputStream.readUTF();
32             System.out.println("from client..." + request);
33
34             // 响应客户端
35             dataOutputStream = new DataOutputStream(socket.getOutputStream());
36             String response = "天气:晴朗,温度:36度";
37             dataOutputStream.writeUTF(response);
38
39         } catch (IOException e) {
40             e.printStackTrace();
41         } finally {
42             try {
43                 if (dataInputStream != null) {
44                     dataInputStream.close();
45                 }
46                 if (dataOutputStream != null) {
47                     dataOutputStream.close();
48                 }
49             } catch (IOException e) {
50                 e.printStackTrace();
51             }
52         }
53     }
54 }

服务端代码很简单,这里没有直接使用InputStream和OutputStream两个流,而是使用了DataInputStream和DataOutputStream两个类,通过readUTF()和writeUTF()两个方法免去转码的痛苦。

客户端代码

 1 package cn.hucc.socket.client;
 2
 3 import java.io.DataInputStream;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.net.Socket;
 7
 8 /**
 9  *
10  * @auth hucc
11  * @date 2015年10月10日
12  */
13 public class WeatherClient {
14     private static final String HOST = "127.0.0.1";
15     private static final int PORT = 8888;
16
17     public static void main(String[] args) {
18
19         Socket socket = null;
20         DataInputStream dataInputStream = null;
21         DataOutputStream dataOutputStream = null;
22         try {
23             socket = new Socket(HOST, PORT);
24
25             //给服务端发送请求
26             dataOutputStream = new DataOutputStream(socket.getOutputStream());
27             String request = "北京";
28             dataOutputStream.writeUTF(request);
29
30             dataInputStream = new DataInputStream(socket.getInputStream());
31             String response = dataInputStream.readUTF();
32             System.out.println(response);
33
34         } catch (IOException e) {
35             e.printStackTrace();
36         }finally{
37             try {
38                 if(dataInputStream != null){
39                     dataInputStream.close();
40                 }
41                 if(dataOutputStream != null){
42                     dataOutputStream.close();
43                 }
44                 if(socket != null){
45                     socket.close();
46                 }
47             } catch (IOException e) {
48                 e.printStackTrace();
49             }
50
51         }
52     }
53 }

运行结果

客户端运行结果:

服务端运行结果:

结果分析

客户端和服务端都运行起来了,并且达到了天气查询的效果,但是服务端只服务了一次就停止了,这明显不符合需求,服务端应该响应完客户端之后,继续监听8888端口,等待下一个客户端的连接。

让服务端一直提供服务

将服务端的代码写入死循环中,一直监听客户端的请求。修改服务端的代码:

 1 public static void main(String[] args) throws IOException {
 2
 3         ServerSocket server = null;
 4         Socket socket = null;
 5         DataInputStream dataInputStream = null;
 6         DataOutputStream dataOutputStream = null;
 7         server = new ServerSocket(PORT);
 8         System.out.println("天气服务端已经移动,监听端口:" + PORT);
 9         while(true){
10             try {
11                 socket = server.accept();
12
13                 // 接受客户端请求
14                 dataInputStream = new DataInputStream(socket.getInputStream());
15                 String request = dataInputStream.readUTF();
16                 System.out.println("from client..." + request);
17
18                 // 响应客户端
19                 dataOutputStream = new DataOutputStream(socket.getOutputStream());
20                 String response = "天气:晴朗,温度:36度";
21                 dataOutputStream.writeUTF(response);
22
23             } catch (IOException e) {
24                 e.printStackTrace();
25             } finally {
26                 try {
27                     if (dataInputStream != null) {
28                         dataInputStream.close();
29                     }
30                     if (dataOutputStream != null) {
31                         dataOutputStream.close();
32                     }
33                 } catch (IOException e) {
34                     e.printStackTrace();
35                 }
36             }
37         }
38     }

通过while(true)死循环,服务端一直监听8888端口,由于socket是阻塞的,只有服务端完成了当前客户端的响应,才会继续处理下一个客户端的响应。这样一直让主线线程去处理socket请求不合适,因此需要为服务端加上多线程功能,同时处理多个socket请求。

给服务端加上多线程

修改代码,将服务端的socket处理抽取出来,并且封装到Runnable接口的run方法中。

 1 package cn.hucc.socket.server;
 2
 3 import java.io.DataInputStream;
 4 import java.io.DataOutputStream;
 5 import java.io.IOException;
 6 import java.net.Socket;
 7
 8 /**
 9  *
10  * @auth hucc
11  * @date 2015年10月10日
12  */
13 public class WeatherThread extends Thread {
14
15     private Socket socket;
16
17     public WeatherThread(Socket socket){
18         this.socket = socket;
19     }
20
21     public void run() {
22
23         DataInputStream dataInputStream = null;
24         DataOutputStream dataOutputStream = null;
25         try {
26             // 接受客户端请求
27             dataInputStream = new DataInputStream(socket.getInputStream());
28             String request = dataInputStream.readUTF();
29             System.out.println("from client..." + request+" 当前线程:"+Thread.currentThread().getName());
30
31             // 响应客户端
32             dataOutputStream = new DataOutputStream(socket.getOutputStream());
33             String response = "天气:晴朗,温度:36度";
34             dataOutputStream.writeUTF(response);
35
36         } catch (IOException e) {
37             e.printStackTrace();
38         } finally {
39             try {
40                 if (dataInputStream != null) {
41                     dataInputStream.close();
42                 }
43                 if (dataOutputStream != null) {
44                     dataOutputStream.close();
45                 }
46             } catch (IOException e) {
47                 e.printStackTrace();
48             }
49         }
50     }
51 }

修改服务端,添加多线程功能

 1 package cn.hucc.socket.server;
 2
 3 import java.io.IOException;
 4 import java.net.ServerSocket;
 5 import java.net.Socket;
 6
 7 /**
 8  *
 9  * @auth hucc
10  * @date 2015年10月10日
11  */
12 public class WeatherServer {
13
14     private static final int PORT = 8888;
15
16     public static void main(String[] args) throws IOException {
17
18         ServerSocket server = null;
19         Socket socket = null;
20         server = new ServerSocket(PORT);
21         System.out.println("天气服务端已经移动,监听端口:" + PORT);
22         while(true){
23                 socket = server.accept();
24                 new WeatherThread(socket).start();
25         }
26     }
27 }

此时服务端已经拥有多线程处理能力了,运行结果如下图:

弊端分析

尽管服务端现在已经有了多线程处理能力,但是通过运行结果,我们可以看到,服务端每次接收到客户端的请求后,都会创建一个新的线程去处理,而jvm的线程数量过多是,服务端处理速度会变慢。而且如果并发较高的话,瞬间产生的线程数量也会比较大,因此,我们需要再给服务端加上线程池的功能。

给服务端加上线程池功能

使用java.util.concurrent.Executor类就可以创建一个简单的线程池,代码如下:

 1 package cn.hucc.socket.server;
 2
 3 import java.io.IOException;
 4 import java.net.ServerSocket;
 5 import java.net.Socket;
 6 import java.util.concurrent.Executor;
 7 import java.util.concurrent.Executors;
 8
 9 /**
10  *
11  * @auth hucc
12  * @date 2015年10月10日
13  */
14 public class WeatherServer {
15
16     private static final int PORT = 8888;
17
18     public static void main(String[] args) throws IOException {
19
20         ServerSocket server = null;
21         Socket socket = null;
22         server = new ServerSocket(PORT);
23         System.out.println("天气服务端已经移动,监听端口:" + PORT);
24
25         //FixedThreadPool最多开启3(参数)个线程,多余的线程会存储在队列中,等线程处理完了
26         //再从队列中获取线程继续处理
27         Executor executor = Executors.newFixedThreadPool(3);
28         while(true){
29                 socket = server.accept();
30                 executor.execute(new WeatherThread(socket));
31         }
32     }
33 }

Executor一共有4种线程池实现,这里使用了FixedThreadPool最多开启3(参数)个线程,多余的线程会存储在队列中,等线程处理完了再从队列中获取,继续处理。这样的话无论并发量多大,服务端只会最多3个线程进行同时处理,使服务端的压力不会那么大。

运行结果:

通过运行结果,可以看到线程只开了1,2,3三个线程。

到这里,socket的简易教程便结束了。O(∩_∩)O~~

时间: 2024-10-13 06:59:34

带线程池的socket客户端与服务端的相关文章

c++ Socket客户端和服务端示例版本二

客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include<arpa/inet.h> #include <error.h> #includ

socket 客户端和服务端通信

客户端要连接服务器:首先要知道服务器的IP地址.而服务器里有很多的应用程序,每一个应用程序对应一个端口号 所以客户端想要与服务器中的某个应用程序进行通信就必须要知道那个应用程序的所在服务器的IP地址,及应用程序所对应的端口号 TCP协议:安全稳定,一般不会发生数据丢失,但是效率低.利用TCP发生数据一般经过3次握手(所有效率低,自己百度三次握手) UDP协议:快速,效率高,但是不稳定,容易发生数据丢失(没有经过三次握手,不管服务器有空没空,信息全往服务器发,所有效率搞,但服务器忙的时候就没办法处

c++ Socket客户端和服务端示例版本三(多线程版本)

客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include<arpa/inet.h> #include <error.h> #includ

c#Socket客户端和服务端的信息发送

这是我制作的界面信息,c# Socket通信的简单使用,刚开始学习,不对的地方请大家指教,目前是可以运行的,之后自己在慢慢添加新的东西.同时了解Tcp协议的三次握手.希望对跟我一样的初学者有所帮助. 客户端: 服务端:

java socket 客户端和服务端通信

1.采用阻塞式readUTF():长连接 2.java socket的3个主要方面如下 1)accept 阻塞,直到接受到一个连接,并返回一个客户端对象实例 2)getInputStream() 3)getOutputStream() 客户端代码: public class ClientSocket { public static final String IP="10.100.63.18"; public static final int PORT=667; public stati

c++ Socket客户端和服务端市里

客户端 #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include<arpa/inet.h> #include <error.h> #includ

java Socket通信,客户端与服务端相互发消息

1.通信过程 网络分为应用层,http.ssh.telnet就是属于这一类,建立在传输层的基础上.其实就是定义了各自的编码解码格式,分层如下: 2.Socket连接 上述通信都要先在传输层有建立连接的基础上才能完成,TCP通过三次握手建立连接:   Socket与http的区别可以看下这个: https://www.cnblogs.com/meier1205/p/5971313.html 还有搜索:http下午茶 3.JAVA socket 客户端与服务端互发消息 实现结构图:   客户端和服务

运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接

运用JAVA的concurrent.ExecutorService线程池实现socket的TCP和UDP连接 最近在项目中可能要用到socket相关的东西来发送消息,所以初步研究了下socket的TCP和UDP实现方式,并且结合java1.5的concurrent.ExecutorService类来实现多线程. 具体实现方式见代码: 一.TCP方式: 1.服务端实现方式: TCP的服务端实现方式主要用到ServerSocket类,接收等待客户端连接的方法是accept(); 代码如下:类Sock

Socket通信客户端和服务端代码

这两天研究了下Socket通信,简单实现的客户端和服务端代码 先上winfrom图片,客户端和服务端一样 服务端代码: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; usin