部分文章内容为公开资料查询整理,原文出处可能未标注,如有侵权,请联系我,谢谢。邮箱地址:gnivor@163.com ►►►需要气球么?请点击我吧!

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("连接服务器成功!" + "\n");
             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("接收消息缓存错误\n");
            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 + "\n");
            fileOut.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("接收消息错误" + "\n");
            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("服务器接收消息缓存错误\n");
            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 + "\n");
            fileOut.close();
            soc.close();
        } catch (Exception e) {
            System.out.println("接收消息错误" + "\n");
            e.printStackTrace();
            return;
        }
    }
    
    public static void main(String arg[]) {
        //启动服务器
        new ServerPart().serverStart();
    }
}
View Code

 

 

 

 
posted @ 2015-03-07 10:16  流了个火  阅读(3365)  评论(1编辑  收藏  举报
►►►需要气球么?请点击我吧!►►►
View My Stats