zoukankan      html  css  js  c++  java
  • Java学习笔记--Socket和ServerSocket

    参考资料:

    http://haohaoxuexi.iteye.com/blog/1979837
    http://zhidao.baidu.com/link?url=OeOSa0YbOzSbMVPa8sgPXcwtyyHsWB1lPkh1XopETtNK_lVtbd9lL7NH3qlFxjC-4kNUmCkIXgcfRW7KJq9_FK
    http://www.cnblogs.com/rond/p/3565113.html
    http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html

    TCP/IP客户套接字

    Java中有两种类型的TCP套接字,一种用于服务器,一种用于客户端。
    ServerSocket类型设计成“监听器”,等待客户端连接的到来。因此,ServerSocket用于服务器。
    Socket类用于客户端,它被设计成连接服务器套接字并且初始化协议的交换。

     

    图片来自:http://www.cnblogs.com/rond/p/3565113.html

    Socket

    Socket可以使客户程序与服务器程序通信,使用Socket连接服务器的过程包含以下4个基本的步骤

    (1)创建Socket
    (2)打开连接到Socket的输入/输出流
    (3)按照一定协议对Socket执行读写操作
    (4)关闭Socket

     
    构造函数

    Socket()

    Socket(InetAddress address, int port)throws UnknownHostException, IOException
    Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
    Socket(String host, int port)throws UnknownHostException, IOException
    Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException
     
    除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。
    InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。
     
    Socket方法
    getInetAddress();      远程服务端的IP地址
    getPort();          远程服务端的端口
    getLocalAddress()      本地客户端的IP地址
    getLocalPort()        本地客户端的端口
    getInputStream();     返回与调用的套接字相关联的输入流
    getOutStream();      返回与调用的套接字相关联的输出流
    值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。
     
    Socket状态
    isClosed();            //连接是否已关闭,若关闭,返回true;否则返回false
    isConnect();      //如果曾经连接过,返回true;否则返回false
    isBound();            //如果Socket已经与本地一个端口绑定,返回true;否则返回false
    如果要确认Socket的状态是否处于连接中,下面语句是很好的判断方式。
    boolean isConnection=socket.isConnected() && !socket.isClosed();   //判断当前是否处于连接

    半关闭Socket

    很多时候,我们并不知道在获得的输入流里面到底读多长才结束。下面是一些比较普遍的方法:
    • 自定义标识符(譬如下面的例子,当受到“bye”字符串的时候,关闭Socket)
    • 告知读取长度(有些自定义协议的,固定前几个字节表示读取的长度的)
    • 读完所有数据
    • 当Socket调用close的时候关闭的时候,关闭其输入输出流

     例:

    public class TestPort {
        public static void main(String[] args) throws IOException {
            // TODO Auto-generated method stub
            String hostname  = "www.baidu.com";
            InetAddress addr = InetAddress.getByName(hostname);
            Socket so = new Socket(addr,80);
            System.out.println("远程连接地址:"+so.getInetAddress());
            System.out.println("远程连接端口:"+so.getPort());
            System.out.println("本地连接地址:"+so.getLocalAddress());
            System.out.println("本地连接端口:"+so.getLocalPort());
            InputStream is  = so.getInputStream();
            /*
             *  这部分为客户端操作,需要一台远程服务器
            int c;
            while((c=is.read())!=-1){
                System.out.println("gas");
            }*/
            is.close();
            so.close();
        }
    }

    运行结果:

    远程连接地址:www.baidu.com/119.75.218.70
    远程连接端口:80
    本地连接地址:/192.168.1.160
    本地连接端口:4338
     ServerSocket
    在客户端/服务器端的通信模式中,服务器端用来监听特定端口上客户端的连接。并且可以发送信息,通过ServerSocket类实现,而客户端通过上面的Socket类实现。
     
    构造函数
    ServerSocket()throws IOException
    ServerSocket(int port)throws IOException
    ServerSocket(int port, int backlog)throws IOException
    ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException
     
    注意点:
    1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP
    2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。
    3. 如果设置端口为0,则系统会自动为其分配一个端口;
    4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。
    5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。
     
    ServerSocket类可以通过getInetAddress方法返回此服务器套接字的本地地址,该方法要绑定一个本地的InetAddress,可以在构造方法 
    ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException 中通过参数bindAddr传递。
    getLocalPort方法用来返回此套接字在其上侦听的接口。
     
    :返回服务器端信息
    public class GetServerIP {
        public static void main(String[] args) throws Exception{
            // TODO Auto-generated method stub
            InetAddress addr =InetAddress.getByName("localhost");
            ServerSocket se = new ServerSocket(10000,10,addr);
            System.out.println(se.getInetAddress());
            System.out.println(se.getLocalPort());    
        }
    }

    运行结果

    localhost/127.0.0.1
    10000

    附:完整例子

    发送端 socket

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    public class SocketSender {
        public static void main(String[] args) {
            Socket socket = null;
            try {
                socket = new Socket("127.0.0.1", 27401);
                // 向服务端发送信息
                OutputStream outer = socket.getOutputStream();
                byte[] b = "客户端:向服务端发送文字,"这是一行测试...."".getBytes();
                outer.write(b);
                outer.flush();
                System.out.println("发送完毕!");
    
                // 接收服务端的返回值
                InputStream inner = socket.getInputStream();
                int count = 0;
                while (count == 0) {
                    count = inner.available();
                }
                byte[] recv = new byte[count];
                inner.read(recv);
    
                String str_recv = new String(recv);
                System.out.println("客户端:接收到服务端的文字:" + str_recv);
            } catch (IOException e) {
                System.out.println("发送端出现异常");
            } finally {
                if (socket != null)
                    try {
                        socket.close();
                    } catch (IOException e) {
                        System.out.println("发送端 finally 出现异常");
                    }
            }
        }
    }

    接收端:ServerSocket

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class SocketReceiver {
        public static void main(String[] args) {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(27401);
                while (true) {
                    Socket socket = serverSocket.accept();
    
                    // 接收客户端的信息
                    InputStream in = socket.getInputStream();
                    int count = 0;
                    while (count == 0) {
                        count = in.available();
                    }
                    byte[] b = new byte[count];
                    in.read(b);
                    String str = new String(b);
                    System.out.println(str);
    
                    // 向客户端发送确认消息
                    OutputStream outer = socket.getOutputStream();
                    byte[] b_out = "已经收到,返回消息码200".getBytes();
                    outer.write(b_out);
                    outer.flush();
    
                    // 关闭socket
                    socket.close();
                }
            } catch (IOException e) {
                System.out.println("接收端出现异常");
            } finally {
                if (serverSocket != null)
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        System.out.println("接收端 finally 出现异常");
                    }
            }
        }
    }

    接收端:方法2,利用多线程,每一个发送端对应接收端的一个线程

    public class SocketReceiverWithThread {
        public static void main(String[] args) {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(27401);
                while (true) {
                    Socket socket = serverSocket.accept();
                    new Thread(new Handler(socket)).start();
                    //socket.close(); 注意 关闭socket不能在这里,而应该写在线程内,否则可能线程没结束就关闭了socket
                }
    
            } catch (IOException e) {
                System.out.println("接收端出现异常");
            } finally {
                if (serverSocket != null)
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        System.out.println("接收端 finally 出现异常");
                    }
            }
        }
    }
    
    class Handler implements Runnable {
        private Socket socket;
        public Handler(Socket socket) {
            this.socket = socket;
        }
        public void run() {
            try {
                // 接收客户端的信息
                InputStream in = socket.getInputStream();
                int count = 0;
                while (count == 0) {
                    count = in.available();
                }
                byte[] b = new byte[count];
                in.read(b);
                String str = new String(b);
                System.out.println(str);
    
                // 向客户端发送确认消息
                OutputStream outer = socket.getOutputStream();
                byte[] b_out = "已经收到,返回消息码200".getBytes();
                outer.write(b_out);
                outer.flush();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭socket
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    附2:使用socket进行文件传送

    客户端 ClienPart.java

    package com.client;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.Socket;
    
    public class ClientPart {
        private Socket socket=null;
        private String ip ;// 设置成服务器IP
        private int port ;
    
        public ClientPart(String ip,int port) {        
            this.ip=ip;
            this.port=port;
            
        }
        private boolean createConnection() {
            try {
                socket = new Socket(ip, port);
                 System.out.print("连接服务器成功!" + "
    ");
                 return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        //向服务器发送消息
        private void sentMessage(String msg){
            if (socket == null)
                return;
            Writer writer;
            try {
                writer = new OutputStreamWriter(socket.getOutputStream());
                writer.write(msg);  
                writer.flush();    //写完后要记得flush  
                writer.close();  
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }  
        }
        private void getFile() {
            if (socket == null)
                return;
            DataInputStream inputStream = null;
            try {
                inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            } catch (Exception e) {
                System.out.print("接收消息缓存错误
    ");
                return;
            }       
            try {                        
                //本地保存路径,文件名会自动从服务器端继承而来。
                String savePath = "F:\client\";
                int bufferSize = 8192;
                byte[] buf = new byte[bufferSize];
                long len=0;
                
                savePath += inputStream.readUTF(); //读取文件名
                DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
                len = inputStream.readLong();    //读取文件长度
                
                System.out.println("文件的长度为:" + len);
                System.out.println("开始接收文件!");
                        
                while (true) {
                    int read = 0;
                    if (inputStream != null) {
                        read = inputStream.read(buf);
                    }
                    if (read == -1) {
                        break;
                    }
                    //客户端读出文件
                    fileOut.write(buf, 0, read);
                }
                
                System.out.println("接收完成,文件存为" + savePath + "
    ");
                fileOut.close();
                socket.close();
            } catch (Exception e) {
                System.out.println("接收消息错误" + "
    ");
                return;
            }
        }
        
        private void putFile(String filename){
            String filePath = filename;
            File client_file = new File(filePath); //要传输的文件路径  
            long filelen = client_file.length();
            System.out.println("要上传的文件长度:" + (int)filelen);
            try{
                DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
                DataOutputStream out_stream = new DataOutputStream(socket.getOutputStream());
                out_stream.writeUTF(client_file.getName());
                out_stream.flush();
                out_stream.writeLong((long) client_file.length());
                out_stream.flush();
    
                int bufferSize = 8192;
                byte[] buf = new byte[bufferSize];
    
                while (true) {
                    int read = 0;
                    if (in_stream != null) {
                        read = in_stream.read(buf);
                    }
                    if (read == -1) {
                        break;
                    }
                    out_stream.write(buf, 0, read);
                }
                out_stream.flush();
                // 注意关闭socket链接,不然客户端会等待server的数据过来,
                // 直到socket超时,会导致数据不完整。                
                in_stream.close();
                socket.close();
                System.out.println("文件传输完成");
            }catch(Exception e){
                e.printStackTrace();
            }
            
        }
    
        public static void main(String arg[]) {
           ClientPart client = new ClientPart("127.0.0.1",7005);
           client.createConnection();
           client.sentMessage("read");//向服务器发送信息,说明需要读取文件
           client.createConnection();
           client.getFile(); //获取服务器发送的文件
           
           client.createConnection();
           client.sentMessage("write");//向服务器发送信息,说明需要上传文件
           client.createConnection();
           client.putFile("F:\client\1.txt");//向服务器发送文件
    
        }
    }
    View Code

    服务端 ServerPart.java

    package com.server;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    
    public class ServerPart {
        int port = 7005;
        public void serverStart() {        
            try {
                ServerSocket ssocket = new ServerSocket(port);
                System.out.println("服务器准备完毕!");
                while (true) {                
                    String msg = checkIO(ssocket); //获取来自客户端的请求是读/写
                    if(msg.compareTo("read")==0){
                        sendFile(ssocket); //向客户端发送文件
                    }
                    else if(msg.compareTo("write")==0){
                        saveFile(ssocket); //存储来自客户端的文件
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //服务器检查来自客户端的请求是读/写
        public String checkIO(ServerSocket ssocket) throws IOException{
            Socket socket=ssocket.accept();
            Reader reader = new InputStreamReader(socket.getInputStream());
            char chars[] = new char[10];  
            int len;  
            StringBuilder sb = new StringBuilder();  
            while ((len=reader.read(chars)) != -1) {  
               sb.append(new String(chars, 0, len));  
            }
            reader.close();  
            socket.close();                        
            return sb.toString();
        }
    
        public void sendFile(ServerSocket ssocket) throws IOException{
            Socket soc = null;
            // 选择进行传输的文件
            String filePath = "F:\server\info.bin";
            File myfile = new File(filePath);
            System.out.println("服务器文件长度:" + (int) myfile.length());
            soc = ssocket.accept();
            System.out.println("建立socket链接");
    
            DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
            DataOutputStream out_stream = new DataOutputStream(soc.getOutputStream());
            //将文件名及长度传给客户端。这里要真正适用所有平台,例如中文名的处理,还需要加工,具体可以参见Think In Java 4th里有现成的代码。
            out_stream.writeUTF(myfile.getName());
            out_stream.flush();
            out_stream.writeLong((long) myfile.length());
            out_stream.flush();
    
            int bufferSize = 8192;
            byte[] buf = new byte[bufferSize];
    
            while (true) {
                int read = 0;
                if (in_stream != null) {
                    read = in_stream.read(buf);
                }
                if (read == -1) {
                    break;
                }
                out_stream.write(buf, 0, read);
            }
            out_stream.flush();
            // 注意关闭socket链接哦,不然客户端会等待server的数据过来,
            // 直到socket超时,导致数据不完整。       
            out_stream.close();
            in_stream.close();
            soc.close();                
            System.out.println("服务器文件传输完成");
        }
        
        //服务器保存文件的方法
        public void saveFile(ServerSocket ssocket) throws IOException{
            Socket soc = ssocket.accept();
            if (soc == null)
                return;
            DataInputStream inputStream = null;
            try {
                inputStream = new DataInputStream(new BufferedInputStream(soc.getInputStream()));
            } catch (Exception e) {
                System.out.print("服务器接收消息缓存错误
    ");
                return;
            }
            
            try {            
                //服务器文件的保存路径
                String savePath = "f:\server\";
                int bufferSize = 8192;
                byte[] buf = new byte[bufferSize];
                long len=0;
                
                savePath += inputStream.readUTF(); //读取文件名
                DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
                len = inputStream.readLong();    //读取文件长度
                //控制台输出
                System.out.println("文件的长度为:" + len);
                System.out.println("开始接收文件!");
                
                while (true) {
                    int read = 0;
                    if (inputStream != null) {
                        read = inputStream.read(buf);
                    }
                    if (read == -1) {
                        break;
                    }
                    //客户端读出文件
                    fileOut.write(buf, 0, read);
                }
                System.out.println("接收完成,文件存为" + savePath + "
    ");
                fileOut.close();
                soc.close();
            } catch (Exception e) {
                System.out.println("接收消息错误" + "
    ");
                e.printStackTrace();
                return;
            }
        }
        
        public static void main(String arg[]) {
            //启动服务器
            new ServerPart().serverStart();
        }
    }
    View Code
     
  • 相关阅读:
    java调用restful webservice(转)
    精心挑选的12款优秀 jQuery Ajax 分页插件和教程
    android shape的使用
    android 获取资源文件 r.drawable中的图片转换为drawable、bitmap
    jquery+ajax分页
    ImageButton自定义按钮的按下效果的高效实现方法(非一般)
    ActionBarSherlock SlidingMenu整合,解决SlidingMenu example的getSupportActionBar()方法不能用问题
    sdk manager更新失败,显示Download interrupted: read timed out,应该如何解决?
    Android SDK 下载速度慢解决方法
    android 让图片充满整个屏幕
  • 原文地址:https://www.cnblogs.com/gnivor/p/4319862.html
Copyright © 2011-2022 走看看