使用Socket类进行TCP通信

客户端Socket类

TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
表示客户端的类:
java.net.Socket:此类实现客户端套接字。
构造方法
Socket(String host, int port)
创建一个流套接字并将并将其连接到指定主机上的指定端口号。
参数
String host:服务器主机的名称/服务器的IP地址
int port:服务器的端口号
成员方法
OutputStream getOutputStream()返回此套接字的输出流
InputStream getInputStream()返回此套接字的输入流
void close()关闭此套接字
实现步骤

  1. 创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
  2. 使用Socket对象中的getOutputStream()获取网络字节输出流OutputStream对象
  3. 使用网络字节输出流OutputStream对象中的方法write()给服务器发数据
  4. 使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
  5. 使用网络字节输入流InputStream中的read()方法读取服务器回写的数据
  6. 释放资源(Socket)

注意

  • 客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
  • 当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
  • 如果这时服务器没有启动,那么就会抛出异常Exception in thread “main” java.net.ConnectException: Connection refused: connect
  • 如果服务器已经启动,那么就可以进行交互了
public class TCPClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream os = socket.getOutputStream();
        os.write("你好服务器".getBytes());
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes, 0, len));
        socket.close();
    }
}

服务器ServerSocket类

TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据。
表示服务器的类:
java.net.ServerSocket:此类实现服务器套接字
构造方法:
ServerSocket(int port)
创建绑定到特定端口的服务器套接字

  • 服务器必须明确一件事情,必须知道是那个客户端请求的服务器,所以可以使用accept()方法获取到请求的客户端对象Socket

成员方法:
Socket accept()侦听并接收到此套接字的连接

服务器的实现步骤

  1. 创建服务器ServerSocket对象和系统要指定的端口号
  2. 使用ServerSocket对象中的方法accept(),获取到请求的客户端对象Socket
  3. 用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
  4. 使用InputStream对象中的方法read()方法读取客户端发送的数据
  5. 获取网络字节输出流OutputStream对象,使用write()给客户端回写数据
  6. 释放资源(Socket, ServerSocket)
public class TCPServer {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes, 0, len));
        OutputStream os = socket.getOutputStream();
        os.write("收到,谢谢".getBytes());
        socket.close();
        server.close();
    }
}

综合实例

配合使用FileInputStream和Socket类实现客户端、服务器端的文件上传。
客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 8888);
        OutputStream os = socket.getOutputStream();
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\work_space\\demo.mkv"));
        int len = 0;
        byte[] bytes = new byte[1024];
        System.out.println("正在上传......");
        while((len = bis.read(bytes)) != -1){
            os.write(bytes, 0, len);
        }
        /*
        上传完文件,给服务器写一个结束标记
        void shutdownOutput()禁用此套接字的输出流
        对于TCP套接字,任何以前写入的数据都将被发送,并且后跟TCP正常连接中止序列。
         */
        socket.shutdownOutput();
        InputStream is = socket.getInputStream();

        while((len = is.read(bytes)) != -1){
            System.out.println(new String(bytes, 0, len));
        }
        bis.close();
        socket.close();
    }
}

服务器:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        Socket socket = server.accept();
        InputStream is = socket.getInputStream();
        File file = new File("D:\\work_space\\Project01\\src\\demo18net\\fileupload");
        if(!file.exists()){
            file.mkdir();
        }
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file + "\\upload.mkv"));
        int len = 0;
        byte[] bytes = new byte[1024];
        System.out.println("正在接收数据......");
        long start = System.currentTimeMillis();
        while((len = is.read(bytes)) != -1){
            bos.write(bytes, 0, len);
        }
        long end = System.currentTimeMillis();
        System.out.println("上传用时: " + (end - start) + "ms.");
        OutputStream os = socket.getOutputStream();
        os.write("上传完成".getBytes());
        bos.close();
        socket.close();
    }
}

注意:如果客户端的Socket对象没有调用shutdownOutput()方法,服务器的read方法读取不到文件的结束标记,使得read方法进入阻塞状态,一直等待数据读取,于是服务器的后续语句将得不到执行。

文件上传优化

对于上面的示例有以下几个问题:

  1. 文件名称写死,最终导致服务器硬盘只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下:
FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fis);
  1. 循环接收问题。服务器只保存一个文件就关闭了,之后的用户无法再上传,应该使用循环,可以不断接收不同用户的文件。
while(true){
	Socket socket = server.accept();
	//file transfer
}
  1. 效率问题,使用多线程技术同时接收几个用户的上传
SocketServer server = new SocketServer(8888);
while(true){
	Socket socket = server.accept();
	new Thread(new Runnable() {
		@Override
		public void run(){
			//完成文件上传
		}
	});
}

注意:Runnable接口的run方法没有抛出异常,所以要用try-catch块捕捉IOException。

改进后服务器如下:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8888);
        while(true){
            Socket socket = server.accept();
            /*
                使用多线程技术,提高程序效率
                有一个客户端上传文件,就开启一个线程,完成文件的上传
             */
            new Thread(new Runnable() {
                //完成文件的上传
                @Override
                public void run() {
                    try{
                        InputStream is = socket.getInputStream();
                        String path = "D:\\work_space\\Project01\\src\\demo18net\\fileupload\\";
                        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path + System.currentTimeMillis() + ".mkv"));
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        System.out.println(Thread.currentThread().getName() + "正在接收数据......");
                        long start = System.currentTimeMillis();
                        while((len = is.read(bytes)) != -1){
                            bos.write(bytes, 0, len);
                        }
                        long end = System.currentTimeMillis();
                        System.out.println("上传用时: " + (end - start) + "ms.");
                        OutputStream os = socket.getOutputStream();
                        os.write("上传完成".getBytes());
                        bos.close();
                        socket.close();
                    }
                    catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

你可能感兴趣的:(Java,网络,java,socket)