Socket通信原理(多线程)

我们在日常上网时,会经常登录某个网址,发送我们的请求信息,然后从该网站得到我们想要的信息。这个是怎么实现的呢。其实,我们可以把我们自己当成客户端,网站当成服务端。其实这是个相对的概念,就是谁收到请求,谁就是服务端;谁发送请求,谁就是客户端。而发送和接收请求都是基于Socket 对象实现的。而这个过程是怎么样的呢?(这里介绍的是基于TCP/IP 的通信)
下面我先用一个简单的图解表示一下:
Socket通信原理(多线程)_第1张图片
通过上面的图解,我们可以很清晰的看到整个通信的流程。
对于编码来实现的话,我们首先需要编写服务端,然后再编写客户端。

服务端的实现思路:

  1. 首先创建ServerSocket类的对象,并定义端口
  2. 然后利用该对象的accept() 的方法,来等待接收请求,注意这里是阻塞的状态。
  3. 如果接收到请求,就会相应的创建Socket类的对象
  4. 首先我们可以向客户端发送消息,“我们收到了你的请求!”,利用Socket对象的输出流。
  5. 我们利用Scoket对象获取输入流,接收客户端发送的信息。
  6. 关闭相关资源

客户端的实现思路:

  1. 创建客户端的Socket对象,并指定想要连接的服务端的IP地址和端口号
  2. 获取Socket对象的输入流,接收服务端发送的消息
  3. 获取Socket对象的输出流,向服务端发送指定的内容
  4. 关闭相关资源

几个需要知道的概念:

  • IP地址:为实现网络中不同计算机之间的通信,每台机器必须有一个唯一的标识–IP地址
    常见格式:192.168.25.130,本机地址一般为127.0.0.1或者“localhost”
  • 端口号:为了区分不同应用的程序,范围为0~65535, 其中 0-1023 为系统保留,我们要定义最好大于1023.
  • 传输的数据单位:输入和输出流的传输的最小单位是字节,8个bit。
  • TCP:Transmission Control Protocol 传输控制协议
  • IP:Internet Protocol 互联网协议

具体的代码实现:
首先是服务端:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author:Xiao
 * @Date:2021/5/31
 **/
public class Server {

    public static void main(String[] args) {

        try {
            //1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("***服务器即将启动,等待客户端的连接***");
            //2.调用accept()方法开始监听,等待客户端的连接
            Socket socket=serverSocket.accept();
            System.out.println("连接一台客户机");

            //3.获取输入流,并读取客户端信息
            InputStream is = socket.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            // 使用字符缓冲流
            BufferedReader br = new BufferedReader(isr);
            String info=null;
            while((info=br.readLine())!=null){//循环读取客户端的信息。一行一行的读取
                System.out.println("我是服务器,客户端说:"+info);
            }
            socket.shutdownInput();//关闭输入流

            //4.获取输出流,响应客户端的请求
            OutputStream os = socket.getOutputStream();
            // 使用标准打印流
            PrintWriter pw = new PrintWriter(os);
            pw.write("欢迎您!");
            pw.flush();//调用flush()方法将缓冲输出
            socket.shutdownOutput();

            // 5.关闭资源
            br.close();
            is.close();
            isr.close();
            os.close();
            pw.close();
            socket.close();
            serverSocket.close();
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

客户端的是代码实现:

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * @author:Xiao
 * @Date:2021/5/31
 **/
public class Client {
    public static void main(String[] args) {
        try {
            //1.创建客户端Socket,指定服务器地址和端口
            Socket socket=new Socket("localhost", 8888);

            //2.获取输出流,向服务器端发送信息
            OutputStream os=socket.getOutputStream();//字节输出流
            PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流
            pw.write("用户名:alice;密码:789");
            pw.flush();
            socket.shutdownOutput();//关闭输出流

            //3.获取输入流,并读取服务器端的响应信息
            InputStream is=socket.getInputStream();
            BufferedReader br=new BufferedReader(new InputStreamReader(is));
            String info=null;
            while((info=br.readLine())!=null){
                System.out.println("我是客户端,服务器说:"+info);
            }
            socket.shutdownInput();

            //4.关闭资源
            br.close();
            is.close();
            pw.close();
            os.close();
            socket.close();
        }  catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输出片段:

服务端的窗口:
***服务器即将启动,等待客户端的连接***
连接一台客户机
我是服务器,客户端说:用户名:alice;密码:789

客户端的窗口:
我是客户端,服务器说:欢迎您!

考虑的问题:当我们需要用多个客户端连通服务器的时候应该怎么办呢?

其实我们可以采用多线程的方式。我们可以把服务端每接收到一个连接时,就启动一个线程对象,然后去执行相应的任务。我们可以把这个代码块可以写成Thread类。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/*
 * 服务器线程处理类
 */
public class ServerThread extends Thread {
	// 和本线程相关的Socket
	Socket socket = null;

	public ServerThread(Socket socket) {
		this.socket = socket;
	}
	
	//线程执行的操作,响应客户端的请求
	public void run(){
		InputStream is=null;
		InputStreamReader isr=null;
		BufferedReader br=null;
		OutputStream os=null;
		PrintWriter pw=null;
		try {
			//获取输入流,并读取客户端信息
			is = socket.getInputStream();
			isr = new InputStreamReader(is);
			br = new BufferedReader(isr);
			String info=null;
			while((info=br.readLine())!=null){//循环读取客户端的信息
				System.out.println("我是服务器,客户端说:"+info);
			}
			socket.shutdownInput();//关闭输入流
			//获取输出流,响应客户端的请求
			os = socket.getOutputStream();
			pw = new PrintWriter(os);
			pw.write("欢迎您!");
			pw.flush();//调用flush()方法将缓冲输出
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			//关闭资源
			try {
				if(pw!=null)
					pw.close();
				if(os!=null)
					os.close();
				if(br!=null)
					br.close();
				if(isr!=null)
					isr.close();
				if(is!=null)
					is.close();
				if(socket!=null)
					socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

然后在服务端的accept() 的方法下改成while循环,不断的接收新连接的客户端。则代码可以相应的改成:(因为在socket=serverSocket.accept(); 等待连接的时候是阻塞的状态,所以不会一直无限循环下去,导致资源的浪费。)

/*
 * 基于TCP协议的Socket通信
 * 服务器端,多线程实现
 */
public class Server {
	public static void main(String[] args) {
		try {
			//1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
			ServerSocket serverSocket=new ServerSocket(8888);
			Socket socket=null;
			//记录客户端的数量
			int count=0;
			System.out.println("***服务器即将启动,等待客户端的连接***");
			//循环监听等待客户端的连接
			while(true){
				//调用accept()方法开始监听,等待客户端的连接
				socket=serverSocket.accept();
				//创建一个新的线程
				ServerThread serverThread=new ServerThread(socket);
				//启动线程
				serverThread.start();
				
				count++;//统计客户端的数量
				System.out.println("客户端的数量:"+count);
				InetAddress address=socket.getInetAddress();
				System.out.println("当前客户端的IP:"+address.getHostAddress());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

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