精华内容
下载资源
问答
  • private Socket socket; private BufferedReader in; private PrintWriter out; public Mult(Socket s)throws IOException{ socket =s; in=new BufferedReader(new InputStreamReader(s.getInputStream()));...
  • Java编写的简易socket通信,既有单线程socket通信也有多线程socket通信,使用Java原生sdk实现,可以运行。
  • JAVA写的多线程socket通信程序源码.我写的作业。
  • java模拟多线程socket通信

    千次阅读 2017-03-01 16:28:03
    1.socket和serverSocket通信 socket和serverSocket通信简单介绍如下,注意客户端与服务器端是怎么交换数据的: 2.服务器端代码 package com.tl.skyLine.socket; import java.io.DataInputStream; import java.io....

    1.socket通信

    Socket原理机制:
    1.通信的两端都有Socket
    2.网络通信其实就是Socket间的通信
    3.数据在两个Socket间通过IO传输

    socket和serverSocket通信简单介绍如下,注意客户端与服务器端是怎么交换数据的:


    应用多线程实现服务器与多客户端之间的通信:

    ① 服务器端创建ServerSocket,循环调用accept()等待客户端连接
    ② 客户端创建一个socket并请求和服务器端连接
    ③ 服务器端接受请求,创建socket与该客户建立专线连接
    ④ 建立连接的两个socket在一个单独的线程上对话
    ⑤ 服务器端继续等待新的连接

    2.服务器端代码

    package com.tl.skyLine.socket;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * 聊天 服务器端
     * Created by tl on 17/3/1.
     */
    public class ChatServer {
        private int port = 8080;// 默认服务器端口
    
        public ChatServer() {
        }
    
        // 创建指定端口的服务器
        public ChatServer(int port) {
            this.port = port;
        }
    
        // 提供服务
        public void service() {
            int i = 0;
            try {
                // 建立服务器连接,设定客户连接请求队列的长度
                ServerSocket server = new ServerSocket(port, 3);
                while (true) {
                    // 等待客户连接
                    Socket socket = server.accept();
                    i++;
                    System.out.println("第" + i + "个客户连接成功!");
                    new Thread(new ServerThread(socket, i)).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new ChatServer().service();
        }
    }
    
    
    class ServerThread implements Runnable {
        private int index;
        private Socket socket;
    
        public ServerThread(Socket socket, int i) {
            this.socket = socket;
            this.index = i;
        }
    
        // 任务是为一个用户提供服务
        @Override
        public void run() {
            try {
                try {
                    // 读取客户端传过来信息的DataInputStream
                    DataInputStream in = new DataInputStream(socket
                            .getInputStream());
                    // 向客户端发送信息的DataOutputStream
                    DataOutputStream out = new DataOutputStream(socket
                            .getOutputStream());
                    while (true) {
                        // 读取来自客户端的信息
                        String accpet = in.readUTF();
                        System.out.println("第" + index + "个客户端发出消息:" + accpet);
                    }
                } finally {// 建立连接失败的话不会执行socket.close();
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    3.客户端代码

    package com.tl.skyLine.socket;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    import java.net.Socket;
    import java.util.Random;
    import java.util.Scanner;
    
    /**
     * 聊天 客户端
     * Created by tl on 17/3/1.
     */
    public class ChatClient {
        private String host = "localhost";// 默认连接到本机
        private int port = 8080;// 默认连接到端口8080
    
        private String name = String.valueOf(new Random().nextInt(999999));//客户端的名字
    
        public ChatClient() {
    
        }
    
        // 连接到指定的主机和端口
        public ChatClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void chat() {
            try {
                // 连接到服务器
                Socket socket = new Socket(host, port);
                // 设置超时
    //            Socket s = new Socket();
    //            SocketAddress socketAddress = new InetSocketAddress(serverIp, port);
    //            s.connect(socketAddress, timeout);
                try {
                    // 读取服务器端传过来信息的DataInputStream
                    DataInputStream in = new DataInputStream(socket
                            .getInputStream());
                    // 向服务器端发送信息的DataOutputStream
                    DataOutputStream out = new DataOutputStream(socket
                            .getOutputStream());
    
                    // 装饰标准输入流,用于从控制台输入
                    Scanner scanner = new Scanner(System.in);
    
                    while (true) {
                        String send = scanner.nextLine();
                        // 把从控制台得到的信息传送给服务器
                        out.writeUTF("客户端[" + name + "]:" + send);
                        // 读取来自服务器的信息
                        String accpet = in.readUTF();
                        System.out.println(accpet);
                    }
    
                } finally {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            new ChatClient().chat();
        }
    }
    

    以上代码利用Socket对象和ServerSocket对象进行简单的网络交互,即客户端通过DataOutputStream对象的writeUTF()方法向服务器发送消息,服务器利用DataInputStream对象的readUTF()方法读出数据。其中ServerSocket对象的accept()方法是阻塞的方法,它会一直等待,直到有客户端连接,同理,DataInputStream对象的readUTF()方法也是阻塞的方法,它也会一直等待,直到客户端调用writeUTF()方法。

    上面代码中ChatClient.java中,客户端控制台发送信息之后,String accpet = in.readUTF();将会一直阻塞,如果是单线程的话,重新开一个客户端,也是处于阻塞当中,所以将服务端设置为多线程,这样就不会影响下一个客户端的通信。

    4.运行结果:


    展开全文
  • Java多线程实现多用户与服务端Socket通信

    千次阅读 多人点赞 2020-11-02 13:54:36
    Java多线程实现多用户与服务端Socket通信,类似QQ、微信、视频等客户端,多用户与服务器通信。详细记录服务端多线程的实现,目标是多用户(客户端)能够同时与服务器建立连接并通信,避免阻塞,进一步完善TCP的...

    目录

    前言回顾

    一、多用户服务器

    二、使用线程池实现服务端多线程

    1、单线程版本

    2、多线程版本

    三、多用户与服务端通信演示

    四、多用户服务器完整代码

    最后


    前言回顾

    在上一篇《Java多线程实现TCP网络Socket编程(C/S通信)》,我们解决了服务器端在建立连接后,连续发送多条信息给客户端接收的问题,解决办法容易理解,将客户端接收信息的功能集中给线程处理,实现多线程同步进行。

    同理,上一篇结束语留下来一个问题,简而言之,相当于多用户访问服务器资源,服务器应该与各个客户端建立连接,并进行通信对话,就像我们日常使用QQ、微信、视频等客户端,就是多用户与服务器通信的例子。

    而上一篇中服务端只实现了单用户的功能,本篇将解决这个问题,详细记录服务端多线程的实现,目标是多用户(客户端)能够同时与服务器建立连接并通信,避免阻塞,进一步完善TCP的Socket网络通信,运用Java多线程技术,实现多用户与服务端Socket通信!

    Java实现socket通信网络编程系列文章:

    1. 基于UDP协议网络Socket编程(java实现C/S通信案例)https://blog.csdn.net/Charzous/article/details/109016215
    2. 基于TCP协议网络socket编程(java实现C/S通信)https://blog.csdn.net/Charzous/article/details/109016215
    3. Java多线程实现TCP网络Socket编程(C/S通信)https://blog.csdn.net/Charzous/article/details/109283697

    一、多用户服务器

    多用户服务器是指服务器能同时支持多个用户并发访问服务器所提供的服务资源,如聊天服务、文件传输等。

    上一篇的TCPServer是单用户版本,每次只能和一个用户对话。我们可以尝试多用户连接,开启多个客户端,具体操作如下:

    这样就允许同时并行执行多个客户端,测试发现,单用户版本的TCPServer.java程序能同时支持多个用户并发连接(TCP三次握手),但不能同时服务多用户对话,只有前一个用户退出后,后面的用户才能完成服务器连接。

    多线程技术,线程调用的并行执行。

    上一篇提到在java中有两种实现多线程的方法,一是使用Thread类,二是使用Runnable类并实现run()方法。下面将使用Runnable类对服务端相关操作功能进行封装,结合上一篇,就学到了两种多线程实现方法。

    //使用Runnable类,作为匿名内部类
    class Handler implements Runnable {
        public void run() {
       //实现run方法
        }
    }

    服务器面临很多客户的并发连接,这种情况的多线程方案一般是:

    1. 主线程只负责监听客户请求和接受连接请求,用一个线程专门负责和一个客户对话,即一个客户请求成功后,创建一个新线程来专门负责该客户。对于这种方案,可以用上一篇方式new Thread创建线程,但是频繁创建线程需要消耗大量系统资源。所以不采用这种方法。
    2. 对于服务器,一般使用线程池来管理和复用线程。线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待状态。如果有新任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,新任务要么放入队列等待,要么增加一个新线程进行处理。

    显然,我们采用第2种线程池的方法。 常见创建方法如下:

    ExecutorService executorService = Executors.newFixedThreadPool(n);//指定线程数量
    ExecutorService executorService = Executors.newCachedThreadPool();//动态线程池

    接下来就是选择线程池的类型了。 使用第一个固定线程数的线程池,显然不够灵活,第二种方式的线程池会根据任务数量动态调整线程池的大小,作为小并发使用问题不大,但其在实际生产环境使用并不合适,如果并发量过大,常常会引发超出内存错误(OutOfMemoryError),根据我们的应用场景,可以用这个动态调整线程池。

    二、使用线程池实现服务端多线程

    1、单线程版本

    首先,与之前的单线程通信对比一下,下面代码只能实现单用户与服务端通信,如果多用户与服务器通信,则出现阻塞。

        //单客户版本,每次只能与一个用户建立通信连接
        public void Service(){
            while (true){
                Socket socket=null;
                try {
                    //此处程序阻塞,监听并等待用户发起连接,有连接请求就生成一个套接字
                    socket=serverSocket.accept();
    
                    //本地服务器控制台显示客户连接的用户信息
                    System.out.println("New connection accepted:"+socket.getInetAddress());
                    BufferedReader br=getReader(socket);//字符串输入流
                    PrintWriter pw=getWriter(socket);//字符串输出流
                    pw.println("来自服务器消息:欢迎使用本服务!");
    
                    String msg=null;
                    //此处程序阻塞,每次从输入流中读入一行字符串
                    while ((msg=br.readLine())!=null){
                        //如果用户发送信息为”bye“,就结束通信
                        if(msg.equals("bye")){
                            pw.println("来自服务器消息:服务器断开连接,结束服务!");
                            System.out.println("客户端离开。");
                            break;
                        }
                        msg=msg.replace("?","!").replace("?","!")
                                .replace("吗","").replace("吗?","").replace("在","没");
                        pw.println("来自服务器消息:"+msg);
                        pw.println("来自服务器,重复消息:"+msg);
                    }
                }catch (IOException e){
                    e.printStackTrace();
                }finally {
                    try {
                        if (socket!=null)
                            socket.close();//关闭socket连接以及相关的输入输出流
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }
            }
        }

     所以,根据上面的分析,将该单线程版本服务端与客户端通信对话的功能独立处理,由一个线程来处理。这样就不会阻塞主进程的执行。具体实现如下面。

     2、多线程版本

     

    1、创建匿名内部类Handler,实现Runnable类的run方法,将通信对话放到run()里面:

        class Handler implements Runnable {
            private Socket socket;
    
            public Handler(Socket socket) {
                this.socket = socket;
            }
    
            public void run() {
                //本地服务器控制台显示客户端连接的用户信息
                System.out.println("New connection accept:" + socket.getInetAddress());
                try {
                    BufferedReader br = getReader(socket);
                    PrintWriter pw = getWriter(socket);
    
                    pw.println("From 服务器:欢迎使用服务!");
    
                    String msg = null;
                    while ((msg = br.readLine()) != null) {
                        if (msg.trim().equalsIgnoreCase("bye")) {
                            pw.println("From 服务器:服务器已断开连接,结束服务!");
    
                            System.out.println("客户端离开。");
                            break;
                        }
                        pw.println("From 服务器:" + msg);
                        pw.println("来自服务器,重复消息:"+msg);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (socket != null)
                            socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

     2、使用newCachedThreadPool( )动态创建线程池

    线程池作为成员变量:

        //创建动态线程池,适合小并发量,容易出现OutOfMemoryError
        private ExecutorService executorService=Executors.newCachedThreadPool();

     服务端的Service方法中创建新线程,交给线程池处理。

     

        //多客户版本,可以同时与多用户建立通信连接
        public void Service() throws IOException {
            while (true){
                Socket socket=null;
                    socket=serverSocket.accept();
                    //将服务器和客户端的通信交给线程池处理
                    Handler handler=new Handler(socket);
                    executorService.execute(handler);
                }
        }

    三、多用户与服务端通信演示

    之前服务端只支持单用户通信对话时候,新用户发送的信息阻塞,服务器无法返回。

    很有趣发现一点,另外一端结束通信,与此同时,另一端则立即收到服务器的回复信息。

    从显示的时间上初步观察,可以判断之前发送的信息是阻塞在服务端进程,断开一方连接后,服务端才将阻塞队列的信息发送到客户端。那使用多线程之后,结果是怎么样呢?

    动图演示进一步体会:

     

    四、多用户服务器完整代码

    /*
     * TCPThreadServer.java
     * Copyright (c) 2020-11-02
     * author : Charzous
     * All right reserved.
     */
    
    package chapter05;
    
    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class TCPThreadServer {
        private int port =8008;//服务器监听窗口
        private ServerSocket serverSocket;//定义服务器套接字
        //创建动态线程池,适合小并发量,容易出现OutOfMemoryError
        private ExecutorService executorService=Executors.newCachedThreadPool();
    
        public TCPThreadServer() throws IOException{
            serverSocket =new ServerSocket(8008);
            System.out.println("服务器启动监听在"+port+"端口...");
    
        }
    
        private PrintWriter getWriter(Socket socket) throws IOException{
            //获得输出流缓冲区的地址
            OutputStream socketOut=socket.getOutputStream();
            //网络流写出需要使用flush,这里在printWriter构造方法直接设置为自动flush
            return new PrintWriter(new OutputStreamWriter(socketOut,"utf-8"),true);
        }
    
        private BufferedReader getReader(Socket socket) throws IOException{
            //获得输入流缓冲区的地址
            InputStream socketIn=socket.getInputStream();
            return new BufferedReader(new InputStreamReader(socketIn,"utf-8"));
        }
    
        //多客户版本,可以同时与多用户建立通信连接
        public void Service() throws IOException {
            while (true){
                Socket socket=null;
                    socket=serverSocket.accept();
                    //将服务器和客户端的通信交给线程池处理
                    Handler handler=new Handler(socket);
                    executorService.execute(handler);
                }
        }
    
    
        class Handler implements Runnable {
            private Socket socket;
    
            public Handler(Socket socket) {
                this.socket = socket;
            }
    
            public void run() {
                //本地服务器控制台显示客户端连接的用户信息
                System.out.println("New connection accept:" + socket.getInetAddress());
                try {
                    BufferedReader br = getReader(socket);
                    PrintWriter pw = getWriter(socket);
    
                    pw.println("From 服务器:欢迎使用服务!");
    
                    String msg = null;
                    while ((msg = br.readLine()) != null) {
                        if (msg.trim().equalsIgnoreCase("bye")) {
                            pw.println("From 服务器:服务器已断开连接,结束服务!");
    
                            System.out.println("客户端离开。");
                            break;
                        }
    
                        pw.println("From 服务器:" + msg);
                        pw.println("来自服务器,重复消息:"+msg);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        if (socket != null)
                            socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        public static void main(String[] args) throws IOException{
            new TCPThreadServer().Service();
        }
    
    }
    
    
    

    最后

    本篇将解决了服务端多用户通信的问题,详细记录服务端多线程的实现,目标是多用户(客户端)能够同时与服务器建立连接并通信,避免阻塞,进一步完善TCP的Socket网络通信,运用Java多线程技术,实现多用户与服务端Socket通信!简而言之,相当于多用户访问服务器资源,服务器应该与各个客户端建立连接,就像我们日常使用QQ、微信、视频等客户端,就是多用户与服务器通信的例子。

    老问题了,๑乛◡乛๑,好像完成这个之后,可以来实现一个什么有趣的呢?这里停留思考3秒!

    ……

    ……

    ……

    就是:实现一个群组聊天房间,类似QQ、微信的群聊,可以多个用户之间的对话交流,是不是感觉挺有趣的。

    基于本篇多线程技术实现多用户服务器端的功能,是否能够解决群组聊天房间的功能呢?实现这个功能,等待更新下一篇!

    如果觉得不错欢迎“一键三连”哦,点赞收藏关注,有问题直接评论,交流学习!

    分享一波福利,跟我一起学习Java,感兴趣的朋友赶紧报名学习哦!

           据说每五行源代码当中就有一行采用Java编写。作为史上应用最广的编程语言,Java无疑是通往开发的第一道关口,如同作文一般,想写出流畅的文章,需先理解语法架构,字词间的逻辑关系。同理,欲写出可编译的Java语言,必先从了解基础语法开始。 精通Java,横向了解软件开发原理,纵向进军Java编程,总是艺多不压身。

            对于一个 Java 程序员而言,能否熟练掌握多线程编程是判断他优秀与否的重要标准之一。因为并发编程是 Java 语言中最为晦涩的知识点,它涉及操作系统、内存、CPU、编程语言等多方面的基础能力,更为考验一个程序员的内功。

    Java实现socket通信网络编程系列文章:

    1. 基于UDP协议网络Socket编程(java实现C/S通信案例)https://blog.csdn.net/Charzous/article/details/109016215
    2. 基于TCP协议网络socket编程(java实现C/S通信)https://blog.csdn.net/Charzous/article/details/109016215
    3. Java多线程实现TCP网络Socket编程(C/S通信)https://blog.csdn.net/Charzous/article/details/109283697

    我的CSDN博客:https://blog.csdn.net/Charzous/article/details/109440277

    展开全文
  • 主要介绍了Java Socket实现多线程通信功能,结合具体实例形式较为详细的分析了java多线程通信的原理及客户端、服务器端相应实现技巧,需要的朋友可以参考下
  • 基于多线程实现的JavaSocket客户端-服务端点对点异步通信程序代码
  • java socket tcpip多线程网络通信服务器客户端
  • Java Socket 多线程通信

    2013-05-10 10:39:12
    Java Socket 传输自定义对象,服务端可以接受个客户端的连接。
  • Java多线程Socket资料

    千次阅读 2012-03-16 15:40:45
    利用Java,编写安全高效的多线程程序变得简单,而且利用多线程Java的网络包我们可以方便的实现多线程服务器程序。 ---- Java是伴随Internet的大潮产生的,对网络及多线程具有内在的支持,具有网

    From:http://newqinhao.iteye.com/blog/631004


    ---- 摘要:在Java出现之前,编写多线程程序是一件烦琐且伴随许多不安全因素的事情。利用Java,编写安全高效的多线程程序变得简单,而且利用多线程和Java的网络包我们可以方便的实现多线程服务器程序。

    ---- Java是伴随Internet的大潮产生的,对网络及多线程具有内在的支持,具有网络时代编程语言的一切特点。从Java的当前应用看,Java主要用于在Internet或局域网上的网络编程,而且将Java作为主流的网络编程语言的趋势愈来愈明显。实际工作中,我们除了使用商品化的服务器软件外,时常需要按照实际环境编写自己的服务器软件,以完成特定任务或与特定客户端软件实现交互。在实现服务器程序时,为提高程序运行效率,降低用户等待时间,我们应用了在Java Applet中常见的多线程技术。

    ---- 一、Java中的服务器程序与多线程

    ---- 在Java之前,没有一种主流编程语言能够提供对高级网络编程的固有支持。在其他语言环境中,实现网络程序往往需要深入依赖于操作平台的网络API的技术中去,而Java提供了对网络支持的无平台相关性的完整软件包,使程序员没有必要为系统网络支持的细节而烦恼。

    ---- Java软件包内在支持的网络协议为TCP/IP,也是当今最流行的广域网/局域网协议。Java有关网络的类及接口定义在java.net包中。客户端软件通常使用java.net包中的核心类Socket与服务器的某个端口建立连接,而服务器程序不同于客户机,它需要初始化一个端口进行监听,遇到连接呼叫,才与相应的客户机建立连接。Java.net包的ServerSocket类包含了编写服务器系统所需的一切。下面给出ServerSocket类的部分定义。

    public class ServerSocket {
    public ServerSocket(int port)
    throws IOException ;
    public Socket accept() throws IOException ;
    public InetAddress getInetAddress() ;
    public int getLocalPort() ;
    public void close() throws IOException ;
    public synchronized void setSoTimeout
    (int timeout) throws SocketException ;
    public synchronized int
    getSoTimeout() throws IOException ;
    }
    ---- ServerSocket构造器是服务器程序运行的基础,它将参数port指定的端口初始化作为该服务器的端口,监听客户机连接请求。Port的范围是0到65536,但0到1023是标准Internet协议保留端口,而且在Unix主机上,这些端口只有root用户可以使用。一般自定义的端口号在8000到16000之间。仅初始化了ServerSocket还是远远不够的,它没有同客户机交互的套接字(Socket),因此需要调用该类的accept方法接受客户呼叫。Accept()方法直到有连接请求才返回通信套接字(Socket)的实例。通过这个实例的输入、输出流,服务器可以接收用户指令,并将相应结果回应客户机。ServerSocket类的getInetAddress和getLocalPort方法可得到该服务器的IP地址和端口。setSoTimeout和getSoTimeout方法分别是设置和得到服务器超时设置,如果服务器在timout设定时间内还未得到accept方法返回的套接字实例,则抛出IOException的异常。
    ---- Java的多线程可谓是Java编程的精华之一,运用得当可以极大地改善程序的响应时间,提高程序的并行性。在服务器程序中,由于往往要接收不同客户机的同时请求或命令,因此可以对每个客户机的请求生成一个命令处理线程,同时对各用户的指令作出反应。在一些较复杂的系统中,我们还可以为每个数据库查询指令生成单独的线程,并行对数据库进行操作。实践证明,采用多线程设计可以很好的改善系统的响应,并保证用户指令执行的独立性。由于Java本身是"线程安全"的,因此有一条编程原则是能够独立在一个线程中完成的操作就应该开辟一个新的线程。

    ---- Java中实现线程的方式有两种,一是生成Thread类的子类,并定义该子类自己的run方法,线程的操作在方法run中实现。但我们定义的类一般是其他类的子类,而Java又不允许多重继承,因此第二种实现线程的方法是实现Runnable接口。通过覆盖Runnable接口中的run方法实现该线程的功能。本文例子采用第一种方法实现线程。

    ---- 二、多线程服务器程序举例

    ---- 以下是我们在项目中采用的多线程服务器程序的架构,可以在此基础上对命令进行扩充。本例未涉及数据库。如果在线程运行中需要根据用户指令对数据库进行更新操作,则应注意线程间的同步问题,使同一更新方法一次只能由一个线程调用。这里我们有两个类,receiveServer包含启动代码(main()),并初始化ServerSocket的实例,在accept方法返回用户请求后,将返回的套接字(Socket)交给生成的线程类serverThread的实例,直到该用户结束连接。

    //类receiveServer
    import java.io.*;
    import java.util.*;
    import java.net.*;

    public class receiveServer{

    final int RECEIVE_PORT=9090;
    //该服务器的端口号

    //receiveServer的构造器
    public receiveServer() {
    ServerSocket rServer=null;
    //ServerSocket的实例
    Socket request=null; //用户请求的套接字
    Thread receiveThread=null;
    try{
    rServer=new ServerSocket(RECEIVE_PORT);
    //初始化ServerSocket
    System.out.println("Welcome to the server!");
    System.out.println(new Date());
    System.out.println("The server is ready!");
    System.out.println("Port: "+RECEIVE_PORT);
    while(true){ //等待用户请求
    request=rServer.accept();
    //接收客户机连接请求
    receiveThread=new serverThread(request);
    //生成serverThread的实例
    receiveThread.start();
    //启动serverThread线程
    }
    }catch(IOException e){
    System.out.println(e.getMessage());}
    }

    public static void main(String args[]){
    new receiveServer();
    } //end of main

    } //end of class

    //类serverThread
    import java.io.*;
    import java.net.*;

    class serverThread extends Thread {

    Socket clientRequest;
    //用户连接的通信套接字
    BufferedReader input; //输入流
    PrintWriter output; //输出流

    public serverThread(Socket s)
    { //serverThread的构造器
    this.clientRequest=s;
    //接收receiveServer传来的套接字
    InputStreamReader reader;
    OutputStreamWriter writer;
    try{ //初始化输入、输出流
    reader=new InputStreamReader
    (clientRequest.getInputStream());
    writer=new OutputStreamWriter
    (clientRequest.getOutputStream());
    input=new BufferedReader(reader);
    output=new PrintWriter(writer,true);
    }catch(IOException e){
    System.out.println(e.getMessage());}
    output.println("Welcome to the server!");
    //客户机连接欢迎词
    output.println("Now is:
    "+new java.util.Date()+" "+
    "Port:"+clientRequest.getLocalPort());
    output.println("What can I do for you?");
    }
    public void run(){ //线程的执行方法
    String command=null; //用户指令
    String str=null;
    boolean done=false;

    while(!done){
    try{
    str=input.readLine(); //接收客户机指令
    }catch(IOException e){
    System.out.println(e.getMessage());}
    command=str.trim().toUpperCase();
    if(str==null || command.equals("QUIT"))
    //命令quit结束本次连接
    done=true;
    else if(command.equals("HELP")){
    //命令help查询本服务器可接受的命令
    output.println("query");
    output.println("quit");
    output.println("help");
    }
    else if(command.startsWith("QUERY"))
    { //命令query
    output.println("OK to query something!");
    }
    //else if …….. //在此可加入服务器的其他指令
    else if(!command.startsWith("HELP") &&
    !command.startsWith("QUIT") &&
    !command.startsWith("QUERY")){
    output.println("Command not Found!
    Please refer to the HELP!");
    }
    }//end of while
    try{
    clientRequest.close(); //关闭套接字
    }catch(IOException e){
    System.out.println(e.getMessage());
    }
    command=null;
    }//end of run

    ---- 启动该服务器程序后,可用telnet machine port命令连接,其中machine为本机名或地址,port为程序中指定的端口。也可以编写特定的客户机软件通过TCP的Socket套接字建立连接。
    //-------------------------------------------
    /**
    * free software
    * from apusic
    * by www.cn-java.com 2001
    */

    import java.util.LinkedList;

    public class ThreadPool
    {
    static final long IDLE_TIMEOUT = 60000L;

    private String name;
    private int minsize;
    private int maxsize;
    private int nextWorkerId = 0;
    private LinkedList pool = new LinkedList();

    public ThreadPool() {
    this("PooledThread");
    }

    public ThreadPool(String name) {
    this(name, 0, 20);
    }

    public ThreadPool(String name, int minsize, int maxsize) {
    this.name = name;
    this.minsize = minsize;
    this.maxsize = maxsize;
    }

    public synchronized void run(Runnable runner) {
    Worker worker;

    if (runner == null) {
    throw new NullPointerException();
    }

    // get a worker from free list...
    if (!pool.isEmpty()) {
    worker = (Worker) pool.removeFirst();
    } else {
    // ...no free worker available, create new one...
    worker = new Worker(name + "-" + ++nextWorkerId);
    worker.start();
    }

    // ...and wake up worker to service incoming runner
    worker.wakeup(runner);
    }

    // Notified when a worker has idled timeout
    // @return true if worker should die, false otherwise
    synchronized boolean notifyTimeout(Worker worker) {
    if (worker.runner != null) {
    return false;
    }
    if (pool.size() > minsize) {
    // Remove from free list
    pool.remove(worker);
    return true; // die
    }
    return false; // continue
    }

    // Notified when a worker has finished his work and
    // free to service next runner
    // @return true if worker should die, false otherwise
    synchronized boolean notifyFree(Worker worker) {
    if (pool.size() < maxsize) {
    // Add to free list
    pool.addLast(worker);
    return false; // continue
    }
    return true; // die
    }

    // The inner class that implement worker thread
    class Worker extends Thread {
    Runnable runner = null;

    public Worker(String name) {
    super(name);
    }

    synchronized void wakeup(Runnable runner) {
    this.runner = runner;
    notify();
    }

    public void run() {
    for (;;) {
    synchronized (this) {
    if (runner == null) {
    try {
    wait(IDLE_TIMEOUT);
    } catch (InterruptedException e) {}
    }
    }

    // idle timed out, die or put into free list
    if (runner == null) {
    if (notifyTimeout(this))
    return;
    else
    continue;
    }

    try {
    runner.run();
    } finally {
    runner = null;
    if (notifyFree(this))
    return;
    }
    }
    }
    }
    }

    //--------------------------------
    Java 程序中的多线程


    yuking转贴
    2001-08-08 00:00:00
    1422 次浏览


    在 Java 程序中使用多线程要比在 C 或 C++ 中容易得多,这是因为 Java 编程语言提供了语言级的支持。本文通过简单的编程示例来说明 Java 程序中的多线程是多么直观。读完本文以后,用户应该能够编写简单的多线程程序。

    为什么会排队等待?
    下面的这个简单的 Java 程序完成四项不相关的任务。这样的程序有单个控制线程,控制在这四个任务之间线性地移动。此外,因为所需的资源 — 打印机、磁盘、数据库和显示屏 -- 由于硬件和软件的限制都有内在的潜伏时间,所以每项任务都包含明显的等待时间。因此,程序在访问数据库之前必须等待打印机完成打印文件的任务,等等。如果您正在等待程序的完成,则这是对计算资源和您的时间的一种拙劣使用。改进此程序的一种方法是使它成为多线程的。

    四项不相关的任务 class myclass {
    static public void main(String args[]) {
    print_a_file();
    manipulate_another_file();
    access_database();
    draw_picture_on_screen();
    }
    }



    在本例中,每项任务在开始之前必须等待前一项任务完成,即使所涉及的任务毫不相关也是这样。但是,在现实生活中,我们经常使用多线程模型。我们在处理某些任务的同时也可以让孩子、配偶和父母完成别的任务。例如,我在写信的同时可能打发我的儿子去邮局买邮票。用软件术语来说,这称为多个控制(或执行)线程。

    可以用两种不同的方法来获得多个控制线程:

    多个进程
    在大多数操作系统中都可以创建多个进程。当一个程序启动时,它可以为即将开始的每项任务创建一个进程,并允许它们同时运行。当一个程序因等待网络访问或用户输入而被阻塞时,另一个程序还可以运行,这样就增加了资源利用率。但是,按照这种方式创建每个进程要付出一定的代价:设置一个进程要占用相当一部分处理器时间和内存资源。而且,大多数操作系统不允许进程访问其他进程的内存空间。因此,进程间的通信很不方便,并且也不会将它自己提供给容易的编程模型。
    线程
    线程也称为轻型进程 (LWP)。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程要廉价得多。这样,因为线程允许协作和数据交换,并且在计算资源方面非常廉价,所以线程比进程更可取。线程需要操作系统的支持,因此不是所有的机器都提供线程。Java 编程语言,作为相当新的一种语言,已将线程支持与语言本身合为一体,这样就对线程提供了强健的支持。
    使用 Java 编程语言实现线程
    Java 编程语言使多线程如此简单有效,以致于某些程序员说它实际上是自然的。尽管在 Java 中使用线程比在其他语言中要容易得多,仍然有一些概念需要掌握。要记住的一件重要的事情是 main() 函数也是一个线程,并可用来做有用的工作。程序员只有在需要多个线程时才需要创建新的线程。

    Thread 类
    Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类导出的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。下面的代码说明了它的用法:

    创建两个新线程 import java.util.*;

    class TimePrinter extends Thread {
    int pauseTime;
    String name;
    public TimePrinter(int x, String n) {
    pauseTime = x;
    name = n;
    }

    public void run() {
    while(true) {
    try {
    System.out.println(name + ":" + new
    Date(System.currentTimeMillis()));
    Thread.sleep(pauseTime);
    } catch(Exception e) {
    System.out.println(e);
    }
    }
    }

    static public void main(String args[]) {
    TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");
    tp1.start();
    TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");
    tp2.start();

    }
    }



    在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1 秒和 3 秒)在屏幕上显示当前时间。这是通过创建两个新线程来完成的,包括 main() 共三个线程。但是,因为有时要作为线程运行的类可能已经是某个类层次的一部分,所以就不能再按这种机制创建线程。虽然在同一个类中可以实现任意数量的接口,但 Java 编程语言只允许一个类有一个父类。同时,某些程序员避免从 Thread 类导出,因为它强加了类层次。对于这种情况,就要 runnable 接口。

    Runnable 接口
    此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。(不同的部分用黑体表示。)

    创建两个新线程而不强加类层次 import java.util.*;

    class TimePrinter implements Runnable {
    int pauseTime;
    String name;
    public TimePrinter(int x, String n) {
    pauseTime = x;
    name = n;
    }

    public void run() {
    while(true) {
    try {
    System.out.println(name + ":" + new
    Date(System.currentTimeMillis()));
    Thread.sleep(pauseTime);
    } catch(Exception e) {
    System.out.println(e);
    }
    }
    }

    static public void main(String args[]) {
    Thread t1 = new Thread(new TimePrinter(1000, "Fast Guy"));
    t1.start();
    Thread t2 = new Thread(new TimePrinter(3000, "Slow Guy"));
    t2.start();

    }
    }



    请注意,当使用 runnable 接口时,您不能直接创建所需类的对象并运行它;必须从 Thread 类的一个实例内部运行它。许多程序员更喜欢 runnable 接口,因为从 Thread 类继承会强加类层次。

    synchronized 关键字
    到目前为止,我们看到的示例都只是以非常简单的方式来利用线程。只有最小的数据流,而且不会出现两个线程访问同一个对象的情况。但是,在大多数有用的程序中,线程之间通常有信息流。试考虑一个金融应用程序,它有一个 Account 对象,如下例中所示:

    一个银行中的多项活动 public class Account {
    String holderName;
    float amount;
    public Account(String name, float amt) {
    holderName = name;
    amount = amt;
    }

    public void deposit(float amt) {
    amount += amt;
    }

    public void withdraw(float amt) {
    amount -= amt;
    }

    public float checkBalance() {
    return amount;
    }
    }



    在此代码样例中潜伏着一个错误。如果此类用于单线程应用程序,不会有任何问题。但是,在多线程应用程序的情况中,不同的线程就有可能同时访问同一个 Account 对象,比如说一个联合帐户的所有者在不同的 ATM 上同时进行访问。在这种情况下,存入和支出就可能以这样的方式发生:一个事务被另一个事务覆盖。这种情况将是灾难性的。但是,Java 编程语言提供了一种简单的机制来防止发生这种覆盖。每个对象在运行时都有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这样,修订过的 Account 对象(如下所示)将不会遭受像数据损坏这样的错误:

    对一个银行中的多项活动进行同步处理 public class Account {
    String holderName;
    float amount;
    public Account(String name, float amt) {
    holderName = name;
    amount = amt;
    }

    public synchronized void deposit(float amt) {
    amount += amt;
    }

    public synchronized void withdraw(float amt) {
    amount -= amt;
    }

    public float checkBalance() {
    return amount;
    }
    }



    deposit() 和 withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时,另一个函数就被阻塞。请注意,checkBalance() 未作更改,它严格是一个读函数。因为 checkBalance() 未作同步处理,所以任何其他方法都不会阻塞它,它也不会阻塞任何其他方法,不管那些方法是否进行了同步处理。

    Java 编程语言中的高级多线程支持

    线程组
    线程是被个别创建的,但可以将它们归类到线程组中,以便于调试和监视。只能在创建线程的同时将它与一个线程组相关联。在使用大量线程的程序中,使用线程组组织线程可能很有帮助。可以将它们看作是计算机上的目录和文件结构。

    线程间发信
    当线程在继续执行前需要等待一个条件时,仅有 synchronized 关键字是不够的。虽然 synchronized 关键字阻止并发更新一个对象,但它没有实现线程间发信。Object 类为此提供了三个函数:wait()、notify() 和 notifyAll()。以全球气候预测程序为例。这些程序通过将地球分为许多单元,在每个循环中,每个单元的计算都是隔离进行的,直到这些值趋于稳定,然后相邻单元之间就会交换一些数据。所以,从本质上讲,在每个循环中各个线程都必须等待所有线程完成各自的任务以后才能进入下一个循环。这个模型称为屏蔽同步,下例说明了这个模型:

    屏蔽同步 public class BSync {
    int totalThreads;
    int currentThreads;

    public BSync(int x) {
    totalThreads = x;
    currentThreads = 0;
    }

    public synchronized void waitForAll() {
    currentThreads++;
    if(currentThreads < totalThreads) {
    try {
    wait();
    } catch (Exception e) {}
    }
    else {
    currentThreads = 0;
    notifyAll();
    }
    }
    }



    当对一个线程调用 wait() 时,该线程就被有效阻塞,只到另一个线程对同一个对象调用 notify() 或 notifyAll() 为止。因此,在前一个示例中,不同的线程在完成它们的工作以后将调用 waitForAll() 函数,最后一个线程将触发 notifyAll() 函数,该函数将释放所有的线程。第三个函数 notify() 只通知一个正在等待的线程,当对每次只能由一个线程使用的资源进行访问限制时,这个函数很有用。但是,不可能预知哪个线程会获得这个通知,因为这取决于 Java 虚拟机 (JVM) 调度算法。

    将 CPU 让给另一个线程
    当线程放弃某个稀有的资源(如数据库连接或网络端口)时,它可能调用 yield() 函数临时降低自己的优先级,以便某个其他线程能够运行。

    守护线程
    有两类线程:用户线程和守护线程。用户线程是那些完成有用工作的线程。守护线程是那些仅提供辅助功能的线程。Thread 类提供了 setDaemon() 函数。Java 程序将运行到所有用户线程终止,然后它将破坏所有的守护线程。在 Java 虚拟机 (JVM) 中,即使在 main 结束以后,如果另一个用户线程仍在运行,则程序仍然可以继续运行。

    避免不提倡使用的方法
    不提倡使用的方法是为支持向后兼容性而保留的那些方法,它们在以后的版本中可能出现,也可能不出现。Java 多线程支持在版本 1.1 和版本 1.2 中做了重大修订,stop()、suspend() 和 resume() 函数已不提倡使用。这些函数在 JVM 中可能引入微妙的错误。虽然函数名可能听起来很诱人,但请抵制诱惑不要使用它们。

    调试线程化的程序
    在线程化的程序中,可能发生的某些常见而讨厌的情况是死锁、活锁、内存损坏和资源耗尽。

    死锁
    死锁可能是多线程程序最常见的问题。当一个线程需要一个资源而另一个线程持有该资源的锁时,就会发生死锁。这种情况通常很难检测。但是,解决方案却相当好:在所有的线程中按相同的次序获取所有资源锁。例如,如果有四个资源 —A、B、C 和 D — 并且一个线程可能要获取四个资源中任何一个资源的锁,则请确保在获取对 B 的锁之前首先获取对 A 的锁,依此类推。如果“线程 1”希望获取对 B 和 C 的锁,而“线程 2”获取了 A、C 和 D 的锁,则这一技术可能导致阻塞,但它永远不会在这四个锁上造成死锁。

    活锁
    当一个线程忙于接受新任务以致它永远没有机会完成任何任务时,就会发生活锁。这个线程最终将超出缓冲区并导致程序崩溃。试想一个秘书需要录入一封信,但她一直在忙于接电话,所以这封信永远不会被录入。

    内存损坏
    如果明智地使用 synchronized 关键字,则完全可以避免内存错误这种气死人的问题。

    资源耗尽
    某些系统资源是有限的,如文件描述符。多线程程序可能耗尽资源,因为每个线程都可能希望有一个这样的资源。如果线程数相当大,或者某个资源的侯选线程数远远超过了可用的资源数,则最好使用资源池。一个最好的示例是数据库连接池。只要线程需要使用一个数据库连接,它就从池中取出一个,使用以后再将它返回池中。资源池也称为资源库。

    调试大量的线程
    有时一个程序因为有大量的线程在运行而极难调试。在这种情况下,下面的这个类可能会派上用场:

    public class Probe extends Thread {
    public Probe() {}
    public void run() {

    while(true) {
    Thread[] x = new Thread[100];
    Thread.enumerate(x);

    for(int i=0; i<100; i++) {
    Thread t = x[i];
    if(t == null)
    break;
    else
    System.out.println(t.getName() + " " + t.getPriority()
    + " " + t.isAlive() + " " + t.isDaemon());
    }
    }
    }
    }



    限制线程优先级和调度
    Java 线程模型涉及可以动态更改的线程优先级。本质上,线程的优先级是从 1 到 10 之间的一个数字,数字越大表明任务越紧急。JVM 标准首先调用优先级较高的线程,然后才调用优先级较低的线程。但是,该标准对具有相同优先级的线程的处理是随机的。如何处理这些线程取决于基层的操作系统策略。在某些情况下,优先级相同的线程分时运行;在另一些情况下,线程将一直运行到结束。请记住,Java 支持 10 个优先级,基层操作系统支持的优先级可能要少得多,这样会造成一些混乱。因此,只能将优先级作为一种很粗略的工具使用。最后的控制可以通过明智地使用 yield() 函数来完成。通常情况下,请不要依靠线程优先级来控制线程的状态。

    小结
    本文说明了在 Java 程序中如何使用线程。像是否应该使用线程这样的更重要的问题在很大程序上取决于手头的应用程序。决定是否在应用程序中使用多线程的一种方法是,估计可以并行运行的代码量。并记住以下几点:

    使用多线程不会增加 CPU 的能力。但是如果使用 JVM 的本地线程实现,则不同的线程可以在不同的处理器上同时运行(在多 CPU 的机器中),从而使多 CPU 机器得到充分利用。
    如果应用程序是计算密集型的,并受 CPU 功能的制约,则只有多 CPU 机器能够从更多的线程中受益。
    当应用程序必须等待缓慢的资源(如网络连接或数据库连接)时,或者当应用程序是非交互式的时,多线程通常是有利的。
    基于 Internet 的软件有必要是多线程的;否则,用户将感觉应用程序反映迟钝。例如,当开发要支持大量客户机的服务器时,多线程可以使编程较为容易。在这种情况下,每个线程可以为不同的客户或客户组服务,从而缩短了响应时间。

    某些程序员可能在 C 和其他语言中使用过线程,在那些语言中对线程没有语言支持。这些程序员可能通常都被搞得对线程失去了信心。

    参考资料

    请阅读 Scott Oaks 和 Henry Wong 所著的《Java Threads》,以了解如何充分利用 Java 的线程功能。
    查找有关 Thread 类和 Java 2 平台,标准版,API 规范的其他方面的信息。
    作者简介
    Neel V. Kumar 是一位软件工程师,他有八年的面向对象编程经验,所使用的语言为 C++ 和 Java 编程语言。他出生于 Iowa,目前住在加利福尼亚的 Menlo Park,刚刚涉足电信领域。以前他对许多项目提供过咨询服务,乐意与他人分享他的知识。可以通过 neelvk@terway.com 与他联系。
    //---------------------
    Java Socket编程(一)


    孙雯
    2001-07-23 00:00:00
    2949 次浏览


    Socket传输模式

    Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

    到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

    无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

    面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

    SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.

    Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

    到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

    无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

    面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

    SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.
    Java Socket编程(二)


    孙雯
    2001-07-23 00:00:00
    2204 次浏览


    Java面向连接的类

    Sockets有两种主要的操作方式:面向连接的和无连接的.面向连接的sockets操作就像一部电话,他们必须建立一个连接和一人呼叫.所有的事情在到达时的顺序与它们出发时的顺序时一样.无连接的sockets操作就像是一个邮件投递,,没有什么保证,多个邮件可能在到达时的顺序与出发时的顺序不一样.

    到底用哪种模式是邮应用程序的需要决定的.如果可靠性更重要的话,用面向连接的操作会好一些.比如文件服务器需要他们的数据的正确性和有序性.如果一些数据丢失了,系统的有效性将会失去.一些服务器,比如间歇性地发送一些数据块.如果数据丢了的话,服务器并不想要再重新发过一次.因为当数据到达的时候,它可能已经过时了.确保数据的有序性和正确性需要额外的操作的内存消耗,额外的费用将会降低系统的回应速率.

    无连接的操作使用数据报协议.一个数据报是一个独立的单元,它包含了所有的这次投递的信息.把它想象成一个信封吧,它有目的地址和要发送的内容.这个模式下的socket不需要连接一个目的的socket,它只是简单地投出数据报.无连接的操作是快速的和高效的,但是数据安全性不佳.

    面向连接的操作使用TCP协议.一个这个模式下的socket必须在发送数据之前与目的地的socket取得一个连接.一旦连接建立了,sockets就可以使用一个流接口:打开-读-写-关闭.所有的发送的信息都会在另一端以同样的顺序被接收.面向连接的操作比无连接的操作效率更低,但是数据的安全性更高.

    SUN一直是网络建设的支持者,所以在Java中支持sockets就不足为奇了.实际上,Java降低了建立一个sockets程序的难度.每一个传输模式都被封装到了不同的类中.面向连接的类将会首先被我们讨论.
    在Java中面向连接的类有两种形式,它们分别是客户端和服务器端.客户端这一部分是最简单的,所以我们先讨论它.

    列表9.1列出了一个简单的客户端的程序.它向一个服务器发出一个请求,取回一个HTML文档,并把它显示在控制台上.

    9.1一个简单的socket客户端

    import java.io.*;
    import java.net.*;
    /**
    * 一个简单的从服务器取回一个HTML页面的程序
    * 注意:merlin是本地机器的名字
    */
    public class SimpleWebClient {
    public static void main(String args[])
    {
    try
    {
    // 打开一个客户端socket连接
    Socket clientSocket1 = new Socket("merlin", 80);
    System.out.println("Client1: " + clientSocket1);
    // 取得一个网页
    getPage(clientSocket1);
    }
    catch (UnknownHostException uhe)
    {
    System.out.println("UnknownHostException: " + uhe);
    }
    catch (IOException ioe)
    {
    System.err.println("IOException: " + ioe);
    }
    }
    /**
    *通过建立的连接请求一个页面,显示回应然后关闭socket
    */
    public static void getPage(Socket clientSocket)
    {
    try
    {
    // 需要输入和输出流
    DataOutputStream outbound = new DataOutputStream(
    clientSocket.getOutputStream() );
    DataInputStream inbound = new DataInputStream(
    clientSocket.getInputStream() );
    // 向服务器发出HTTP请求
    outbound.writeBytes("GET / HTTP/1.0

    ");
    // 读出回应
    String responseLine;
    while ((responseLine = inbound.readLine()) != null)
    {
    // 把每一行显示出来
    System.out.println(responseLine);
    if ( responseLine.indexOf("") != -1 )
    break;
    }
    // 清除
    outbound.close();
    inbound.close();
    clientSocket.close();
    }
    catch (IOException ioe)
    {
    System.out.println("IOException: " + ioe);
    }
    }
    }
    Java Socket编程(二)
    作者:孙雯 / ASP中华网 [责编:Purple]


    Java面向连接的类

    回忆一个,一个客户端向一个正在监听的服务器socket发出一个连接.客户端的sockets是用Socket类建立的.下面的程序建立了一个客户端的socket并且连接到了一个主机:

    Socket clientSocket = new Socket("merlin", 80);

    第一个参数是你想要连接的主机的名称,第二个参数是端口号.一个主机名称指定了目的的名称.端口号指定了由哪个应用程序来接收.在我们的情况下,必须指定80,因为它是默认的HTTP协议的端口.另外的知名的端口列在表9.1中,看:

    知名的端品:

    echo 7

    daytime 13

    daytime 13

    ftp 21

    telnet 23

    smtp 25

    finger 79

    http 80

    pop3 110

    因为Socket类是面向连接的,它提供了一个可供读写的流接口.java.io包中的类可以用来访问一个已连接的socket:

    DataOutputStream outbound = new DataOutputStream(
    clientSocket.getOutputStream() );
    DataInputStream inbound = new DataInputStream( clientSocket.getInputStream()
    );

    一旦流建立了,一般的流操作就可以做了:

    outbound.writeBytes("GET / HTTP/1.0

    );
    String responseLine;
    while ( (responseLine = inbound.readLine()) != null)
    {
    System.out.println(responseLine);
    }

    以上的小程序请求了一个WEB页面并且把它显示出来.当程序完成之后,连接必须关闭.

    outbound.close();
    inbound.close();
    clientSocket.close();

    注意socket流必须首先关闭.所有的的socket流必须在socket关闭之前关闭.这个小程序非常地简单,但是所有的客户端程序都必须遵首下面的基本的步骤:

    1.建立客户端socket连接.

    2.得到socket的读和写的流.

    3.利用流.

    4.关闭流.

    5.关闭socket.

    使用一个服务器端的socket只是有一点复杂,它将在下面讲到.
    Java Socket编程(三)


    孙雯
    2001-07-23 00:00:00
    1523 次浏览


    服务器Sockets

    列表9.2是一个服务器应用程序的一部分.

    列表9.2 一个简单的服务器程序

    /**
    * 一个监听端口并提供HTML文档的程序.
    */
    class SimpleWebServer {
    public static void main(String args[])
    {
    ServerSocket serverSocket = null;
    Socket clientSocket = null;
    int connects = 0;
    try
    {
    {
    // 建立一个服务器socket
    serverSocket = new ServerSocket(80, 5);
    while (connects < 5)
    {
    // 等待连接
    clientSocket = serverSocket.accept();
    //服务连接
    ServiceClient(clientSocket);
    connects++;
    }
    serverSocket.close();
    }
    catch (IOException ioe)
    {
    System.out.println("Error in SimpleWebServer: " + ioe);
    }
    }
    public static void ServiceClient(Socket client)
    throws IOException
    {
    DataInputStream inbound = null;
    DataOutputStream outbound = null;
    try
    {
    // 得到IO流
    inbound = new DataInputStream( client.getInputStream());
    outbound = new DataOutputStream( client.getOutputStream());
    //格式化输出(回应头和很少的HTML文档)
    StringBuffer buffer = PrepareOutput();
    String inputLine;
    while ((inputLine = inbound.readLine()) != null)
    {
    //如果到了HTTP请求的尾部,就发送回应
    if ( inputLine.equals("") )
    {
    outbound.writeBytes(buffer.toString());
    break;
    }
    }
    }
    finally
    {
    // 清除
    System.out.println("Cleaning up connection: " + client);
    tln("Cleaning up connection: " + client);
    outbound.close();
    inbound.close();
    client.close();
    client.close();
    }
    }
    Java Socket编程(三)
    作者:孙雯 / ASP中华网 [责编:Purple]


    服务器Sockets

    服务器并不是主动地建立连接.相反地,他们是被动地监听一个客户端的连接请示然后给他们服务.服务器是由类ServerSocket来建立的.下面的程序建立了一个服务器端socket并把它绑定到80端口:

    ServerSocket serverSocket = new ServerSocket(80, 5);

    第一个参数是服务器要监听的端口.第二个参数是可选的.API文档中说明了这是一个监听时间,但是在传统的socket程序中第二个参数是监听深度.一个服务器可以同时接收多个连接请求,但是每次只能处理一个.监听堆是一个无回答的连接请求队列.上面的请求建立一个连接来处理最后五个请求.如果省略了后面的一个参数,则默认值是50.

    ServerSocket serverSocket = new ServerSocket(80, 5);

    一旦socket建立了并开始监听连接,进来的连接将会建立并放在监听堆.accetp()方法把在堆中的连接取出来.

    Socket clientSocket = serverSocket.accept();

    这个方法返回一个用来与来访者对话的客户端连接.服务器本身不可能建立对话,相反地,服务器socket会使用accept()方法来产生一个新的socket.服务器socket依旧打开并排列新的连接请求.

    与客户端socket一样,下面的一步建立输入和输出流:

    DataInputStream inbound = new DataInputStream( clientSocket.getInputStream() ); DataOutputStream outbound = new DataOutputStream( clientSocket.getOutputStream() );

    一般的I/O操作可以在新建的流中运用.在服务器回应前它等待客户端发送一个空白的行.当会话结束时,服务器关闭流和客户端socket.如果在队列中没有请示将会出现什么情况呢?那个方法将会等待一个的到来.这个行为叫阻塞.accept()方法将会阻塞服务器线程直到一个呼叫到来.当5个连接处理完闭之后,服务器退出.任何的在队列中的呼叫将会被取消.

    所有的服务器都要有以下的基本的步骤:

    1.建立一个服务器socket并开始监听.

    2.使用accept()方法取得新的连接.

    3.建立输入和输出流.

    4.在已有的协议上产生会话.

    5.关闭客户端流和socket.

    6.回到第二步或者到第七步.

    7.关闭服务器socket.

    Java Socket编程(四)


    孙雯
    2001-07-23 00:00:00
    1439 次浏览


    重复和并发服务器

    这个应用程序被当作一个重复的服务器.因为它只有在处理完一个进程以后才会接受另一个连接.更多的复杂服务器是并发的.它为每一个请求分配一个线程,而不是来一个处理一个.所以看起来它在同时处理多人请求.所有的商业的服务器都是并发的服务器.

    Java数据报类

    不像面向连接的类,数据报的客户端和服务器端的类在表面上是一样的.下面的程序建立了一个客户和服务器商的数据报sockets:

    DatagramSocket serverSocket = new DatagramSocket( 4545 );
    DatagramSocket clientSocket = new DatagramSocket();

    服务器用参数4545来指定端口号,由于客户端将要呼叫服务器,客户端可以利用可利用的端口.如果省略第二个参数,程序会让操作系统分配一个可用的端口.客户端可以请求一个指定的端口,但是如果其它的应用程序已经绑定到这个端口之上,请求将会失败.如果你的意图不是作为一个服务器,最好不要指定端口.

    由于流不能由交谈得到,那么我么如何与一个数据报Socket进行对话.答案在于数据报类.

    接收数据报

    DatagramPacket类是用来通过DatagramSocket类接收和发送数据的类.packet类包括了连接信息和数据.就如前面所说的一样,数据报是自身独立的传输单元.DatagramPacket类压缩了这些单元.下面的程序表示了用一个数据报socket来接收数据:

    DatagramPacket packet = new DatagramPacket(new byte[512], 512); clientSocket.receive(packet);
    clientSocket.receive(packet);

    packet的构建器需要知道将得到的数据放在哪儿.一个512字节的缓存被建立并且作为构建器的第二个参数.每二个构建器参数是缓存的大小.就像ServerSocket类的accept()方法一样,receive()方法在数据可用之前将会阻塞.

    发送数据报

    发送数据报是非常地简单地,所有需要的只是一个地址.地址是由InetAddress类来建立的.这个类没有公共的构建器,但是它有几个static的方法,可以用来建立这个类的实例.下面的列表列出了建立InetAddress类的实例的方法:

    Public InetAddress Creation Methods

    InetAddress getByName(String host);
    InetAddress[] getAllByName(String host);
    InetAddress getLocalHost();

    得到本地主机的地址是非常地有用的,只有前面两个方法是用来发送数据包的.getByName()和getAllByName()需要目的主机的地址.第一个方法仅仅只是返回第一个符合条件的东西.第二个方法是必须的,因为一台计算机可能有多个地址.在这种情况下,这台计算机被称为multi-homed.

    所有的建立的方法都被标记为static.它们必须像下面这样得到调用:

    InetAddress addr1 = InetAddress.getByName("merlin");
    InetAddress addr2[] = InetAddress.getAllByName("merlin");
    InetAddress addr3 = InetAddress.getLocalHost();

    重复和并发服务器

    所有的这些调用都可以掷出一个UnknownHostException违例.如果一台计算机没有连接上DNS服务器,或者主机的确没有找到,这个违例就会被掷出.如果一台计算机没有一个激活的TCP/IP配置,getLocalHost()也为失败并掷出一个违例.

    一旦一个地址被确定了,数据报就可以被送出了.下面的程序传输了一个字符串给目的socket:

    String toSend = "This is the data to send!");
    byte[] sendbuf = new byte[ toSend.length() ];
    toSend.getBytes( 0, toSend.length(), sendbuf, 0 );
    DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,
    addr, port);
    clientSocket.send( sendPacket );

    首先,字符串必须被转换成一个字节数组.然后,一个新的DatagramPacket实例必须被建立.注意构建器的最后两个参数.因为要发送一个包,所以地址和端口必须被给定.一个applet可能可以知道它的服务器的地址,但是服务器如何知道它的客户机的地址呢.当任何一个包被收到后,返回的地址和端口会被解压出来,并通过getAddress()和getPort()方法得到.这就是一个服务器如何回应一个客户端的包:

    DatagramPacket sendPacket = new DatagramPacket( sendbuf, sendbuf.length,
    recvPacket.getAddress(), recvPacket.getPort() );
    serverSocket.send( sendPacket );

    不像面向连接的操作,数据报服务器服务器其实比数据报客户端更简单:

    数据报服务器

    一个数据报服务器的基本步骤:

    1.在一个指定的端口上建立一个数据报socket.

    2.用receive方法等待进来的包.

    3.用特定的协议来回应收到的包.

    4.回到第二步或继续第二步.

    5.关闭数据报socket.

    列表9.3演示了一人简单的数据报回应服务器.它将回应它收到的包.

    列表9.3.一个简单的数据报回应服务器

    import java.io.*;
    import java.net.*;
    public class SimpleDatagramServer
    {
    public static void main(String[] args)
    {
    DatagramSocket socket = null;
    DatagramPacket recvPacket, sendPacket;
    try
    {
    socket = new DatagramSocket(4545);
    while (socket != null)
    {
    recvPacket= new DatagramPacket(new byte[512], 512);
    socket.receive(recvPacket);
    sendPacket = new DatagramPacket(
    recvPacket.getData(), recvPacket.getLength(),
    recvPacket.getAddress(), recvPacket.getPort() );
    socket.send( sendPacket );
    }
    }
    catch (SocketException se)
    {
    System.out.println("Error in SimpleDatagramServer: " + se);
    }
    catch (IOException ioe)
    {
    System.out.println("Error in SimpleDatagramServer: " + ioe);

    Java Socket编程(五)


    孙雯
    2001-07-23 00:00:00
    1316 次浏览


    简单的WEB服务器

    一个简单的WEB服务器将由列表9.2这样构建.当然,还必须要对方法和回应事件进行改进.简单的服务器不会分析和存储请求头.新的WEB服务器将分析和存储请求,为以后的处理作准备.为了达到这个目的,你必须有一个包含HTTP请求的类.

    HTTPrequest类

    列表9.5列出了一个完整的HTTPrequest类.这个类必须包括一个请求头所需的所有信息.

    列表9.5.HTTPrequest类.

    import java.io.*;
    import java.util.*;
    import java.net.*;
    import NameValue;
    /**
    * 这个类有一个HTTP请求的所有信息
    */
    public class HTTPrequest
    {
    public String version;
    public String method;
    public String file;
    public Socket clientSocket;
    public DataInputStream inbound;
    public NameValue headerpairs[];
    /**
    * 建立一个这个类的实例
    */
    public HTTPrequest()
    {
    version = null;
    method = null;
    file = null;
    clientSocket = null;
    inbound = null;
    inbound = null;
    headerpairs = new NameValue[0];
    }
    /**
    * 加入一个名称/值对到核心数组
    */
    public void addNameValue(String name, String value)
    {
    try
    {
    NameValue temp[] = new NameValue[ headerpairs.length + 1 ];
    System.arraycopy(headerpairs, 0, temp, 0, headerpairs.length);
    temp[ headerpairs.length ] = new NameValue(name, value);
    headerpairs = temp;
    }
    catch (NullPointerException npe)
    {
    System.out.println("NullPointerException while adding name-value:
    " + npe);
    }
    }
    /**
    * 以字符串的形式归还这个类
    */
    public String toString()
    {
    String s = method + " " + file + " " + version + "
    ";
    for (int x = 0; x < headerpairs.length; x++ )
    s += headerpairs[x] + "
    ";
    return s;
    }
    }

    NameValue类简单地存储了两个字符串:name 和 value.当一个新的对要被加入时,一个新的数组将被分配.新的数组接受了旧的数组和新的成员.旧的数组然后被一个新建的对象覆盖了.
    展开全文
  • Java多线程Socket的实现

    千次阅读 2019-06-11 11:59:13
    应用多线程来实现服务器与多客户端之间的通信 工具类 public class ThreadUtils extends Thread{ private Socket socket = null; public ThreadUtils (Socket socket){//创建构造方法 this.socket = socket; ...

    应用多线程来实现服务器与多客户端之间的通信

    1.基于TCP协议的Socket实现

    在这里插入图片描述

    工具类

    public class ThreadUtils extends Thread{
    
        private Socket socket = null;
    
        public ThreadUtils (Socket socket){//创建构造方法
            this.socket = socket;
        }
    
        public void run() {
            InputStream is = null;
            InputStreamReader isr = null;
            BufferedReader bf = null;
            try {
                is = socket.getInputStream(); //获得字节输入流
                isr = new InputStreamReader(is);//将字节输入流转为 字符输入流
                bf = new BufferedReader(isr); //将字符输入流加入缓冲区
                String data = null;//返回的数据
                while (bf.readLine()!=null){
                    data = bf.readLine(); //读取数据
                }
                socket.shutdownInput();//关闭输入流
    
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    if(bf!=null)
                        bf.close();
                    if(isr!=null)
                        isr.close();
                    if(is!=null)
                        is.close();
                    if(socket!=null)
                        socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
    
        }
    }
    

    Action类

    在这里插入图片描述

    2.基于UDP协议的Socket实现

    UDP协议(用户数据包协议)是无连接、不可靠、无序的。
    速度相对而言比较快,进行传输的时候,首先会将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去。


    相关操作类:


    1.DatagramPacket :表示数据报包
    2.DatagramSocket: 进行端到端通信的类

    在这里插入图片描述

    在这里插入图片描述

    具体的实现

    在这里插入图片描述
    如果想要响应客户端 , 则可以调用DatagramPacket.getInetAddress(); 这个方法拿到客户端的Inetaddress实例
    在发送数据报回去

    在这里插入图片描述

    传输一个对象可以使用ObjectOutputStream
    在这里插入图片描述

    展开全文
  • 简单模拟多线程Socket通信(java)

    千次阅读 2012-09-03 09:36:38
    先来看一段单线程的原始代码(代码中有详细的注释):    服务器(TCPServer.java): import java.net.*; import java.io.*; public class TCPServer{ public static void main(String[] args) throws ...
  • java多线程socket实现简单的聊天

    千次阅读 2017-10-24 10:04:26
    server端:需要注意的是死循环acept,然后对每一个客户端开两个线程,分别是读写,这样使得可以全双工通信,另外注意读写的方法,比如读的时候的nextLine,要记得在发送的时候加上换行符,否则读不到。 package ...
  • 一个简单的 Java 多线程 Socket 代理服务器。 它侦听传入的连接并将任何通信转发到服务器,同时记录整个对话。 套接字代理服务器 这是库的主类。 它可以由第三方实例化,并提供本地端口和服务器的远程主机/端口以将...
  • 很多时候在javaweb项目中我们需要用到Socket通信来实现功能,在web中使用Socket我们需要建立一个监听程序,在程序...接下来通过本文给大家介绍Java Web项目中使用Socket通信多线程、长连接的方法,感兴趣的朋友一起学习
  • Java多线程实现TCP网络Socket编程(C/S通信)

    万次阅读 多人点赞 2020-10-26 09:45:38
    本篇详细记录实现java多线程通信,目标达到客户端可以一次接收服务器发送的多条信息,避免阻塞。将客户端接收信息功能独立为一个线程来完成,进一步完善TCP的Socket网络通信,C/S软件架构的程序设计!
  • java Socket通信多线程的关系

    千次阅读 2018-10-02 09:34:31
    项目背景: 矿山的定位系统 矿工下井时,经过基站,程序要及时...多线程socket、sql批处理 缺陷:耗时长,需要改进; 误区:虽然一个基站,但是手卡多个,需要多个连接,涉及多线程java代码 package cn....
  • java实现多线程socket通信技术

    千次阅读 2014-09-23 14:38:36
    在我们前面一个例子中,我们使用下面这样一段代码 ... import java.io.DataInputStream; import java.io.InputStream; import java.net....import java.net.Socket; public class EchoServer { /** * 打
  • 下面是一个简单的UDP Socket通信的例子,使用到了多线程 UDP协议的特点:无连接、不可靠、无序,所以是有可能出现丢包的情况的。 过程如下: 服务器端 创建DatagramSocket,指定端口号 创建DatagramPacket ...
  • 简单的java socket通信个客户端同时连接,功能可在此基础上进行扩展。效果如图:server:package com.lb.LB_Socket;import java.io.BufferedReader; import java.io.IOException; import java.io....
  • 觉得socket不难,做才是提高的方式。 以下是代码: 服务端: package lance.socket_2; import java.io.*; import java.net.*; //练习一下而已 public class ServerTest { public static void main(Str
  • Socket是一个有效的网络通信的工具,下面这个例子给大家演示一下如何使用Socket来做服务器与客户端之间的通信客户端代码/** * @author Jian * 客户端 */ public class Client { public static void main(String...
  • java nio socket实现多线程多用户通信

    千次阅读 2013-10-11 20:56:27
    本程序在客户端异常退出后有时会在服务器端有死循环,望读者知悉 server端代码: ... import java.io.IOException; import java.net.InetSocketAddress;...import java.net.Socket; import java.nio.ByteBuffer; im
  • android4.0以后的版本对于在主线程中实现Socket
  • Socket多线程处理类 /** * Socket多线程处理类 */ public class SocketThread extends Thread { private Socket socket; public SocketThread(Socket socket) { this.socket = socket; ...
  • Java Socket实战之二 多线程通信

    万次阅读 多人点赞 2012-02-14 21:43:22
    上一篇文章说到怎样写一个最简单的Java Socket通信,但是在上一篇文章中的例子有一个问题就是Server只能接受一个Client请求,当第一个Client连接后就占据了这个位置,后续Client不能再继续连接,所以需要做些改动,...
  • java socket通信

    2011-06-18 23:28:25
    Socket多线程通信,通过线程池是多线程通信,实现socket通信协议等。
  • java tcp多线程通信

    2009-07-23 11:02:56
    java tcp socket 多线程多句通信
  • Java多线程-----Socket通信

    千次阅读 2005-07-07 18:08:00
    一有客户端连接就创建两个新的线程来负责这个连接一个负责客户端发送的信息(ClientMsgCollectThread 类),另一个负责通过该Socket发送数据(ServerMsgSendThread )Server.java代码如下:/* * 创建日期 2005-7-7 *...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 73,289
精华内容 29,315
关键字:

java多线程socket通信

java 订阅