Java Socket编程

什么是Socket

Socket代表通过互联网进行双向通信的两个程序的其中一端,Socket需要绑定一个端口,这样TCP层才能知道应该把数据发送给哪个进程。JAVA中的Socket是对TCP协议的封装。底层是使用TCP协议实现的

以C/S架构为例

在Server端,在创建Server时需要绑定一个端口号,例如8080。之后Server端监听这个端口收到的数据

在Client端,Client需要使用Server的主机名和Server绑定的端口号来来创建一个Socket来代表Server端,并来发起连接请求。Client端不需要主动绑定指定的端口号,操作系统会自动分配一个端口号给Client端。

Server端收到Client的连接请求之后,会使用Client端的主机名和端口号创建一个Socket来代表Client端。

image-20201021165701584

代码示例
Server端
public static void main(String[] args) {
    try {
        ServerSocket serverSocket = new ServerSocket(8080);
        Socket socket = serverSocket.accept();
        System.out.println("" + socket.getLocalPort() + ", " + socket.getPort());
        socket.close();
        serverSocket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Client端
public static void main(String[] args) {
    try {
        Socket socket = new Socket("localhost", 8080);
        System.out.println("" + socket.getLocalPort() + ", " + socket.getPort());
        socket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
运行结果

先运行Server端,再运行Client端

Client端运行结果如下

61420, 8080

Process finished with exit code 0

Server端运行如下

8080, 61420

Process finished with exit code 0

使用Socket实现Client端和Server端的通信

实现Client向Server发送Hello Server!, Server端回复Hello Client给Client。

Client端如下
  1. 创建Socket
  2. 打开Socket的输入/出流
  3. 向输出流中写入"Hello Server!"
  4. 读取输入流,读到数据后跳出循环。
  5. 关闭资源
public static void main(String[] args) {
    try (Socket socket = new Socket("localhost", 8080);
         BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        ){
        writer.println("Hello Server!");
        String str;
        while ((str = reader.readLine()) != null) { // 当Socket没有被关闭时,返回的值为非null,如果Socket被关闭,返回的时null
            System.out.println(str);
            break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Server端如下
  1. 创建Server
  2. 获取代表Client的Socket
  3. 打开Client Socket的输入/出流
  4. 读取输入流内容,读到内容后,写"Hello Client!"到输出流
  5. Client关闭资源后,Server也会关闭
public static void main(String[] args) {
    try (ServerSocket serverSocket = new ServerSocket(8080);
         Socket socket = serverSocket.accept();
         BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
         PrintWriter writer = new PrintWriter(socket.getOutputStream(), true)) {
        String str;
        while ((str = reader.readLine()) != null) {
            System.out.println(str);
            writer.println("Hello Client!");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
运行结果如下

先运行Server,再运行Client

Client端
Hello Client!

Process finished with exit code 0
Server端
Hello Server!

Process finished with exit code 0

模拟WebServer

创建一个Server返回用户通过浏览器访问的页面。

例如用户访问http://localhost:8080/index.html时,我们应该返回index.html给浏览器

首先看看在浏览器中访问http://localhost:8080/index.html,Server端收到的是什么

创建一个Server,监听8080, 并打印出收到的数据

package Sample_4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080);
             Socket socket = serverSocket.accept();
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));) {
            String str;
            while ((str = reader.readLine()) != null) {
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

先运行Server, 然后在在浏览器中访问http://localhost:8080/index.html

运行结果如下

GET /index.html HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

我们只需要关注并解析第一行

GET /index.html HTTP/1.1
实现Server

Server的执行流程是:

  1. 从第一行中解析出请求的资源,如index.html
  2. 如果能找到这个资源,向Socket的输出流中写请求结果给浏览器
    1. 写响应行:HTTP/1.1 200 OK\r\n
    2. 写响应头:content‐Type:text/html\r\n
    3. 写空行:\r\n
    4. 把资源写到Socket的输出流
  3. 如果没有找到这个资源,向Socket的输出流中写请求结果给浏览器
    1. 写响应行:HTTP/1.1 404 OK\r\n

代码实现如下

package Sample_4;

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

public class Server {

    public static void transferOut(File file, OutputStream out) throws IOException {
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            in.transferTo(out);
        } finally {
            Objects.requireNonNull(in).close();
        }
    }

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080);
             Socket socket = serverSocket.accept();
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             OutputStream out = socket.getOutputStream()
        ) {
            String str;
            while ((str = reader.readLine()) != null) {
                String[] params = str.split(" ");
                String path = params[1].substring(1);
                File file = new File(path);
                if (file.exists()) {
                    out.write("HTTP/1.1 200 OK\r\n".getBytes());
                }
                else {
                    file = new File("404.html");
                    out.write("HTTP/1.1 404 Not Found\r\n".getBytes());
                }
                out.write("content‐Type:text/html\r\n".getBytes());
                out.write("\r\n".getBytes());
                transferOut(file, out);
                break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

准备一个index.html和404.html

image-20201022112456780然后运运行Server, 然后从浏览器中访问

改进Server

现在我们创建的Server只能处理一次请求,处理完就关闭Server了

我们需要把他改成可以处理多个请求。思路就是每次收到Client的连接请求就创建一个线程来处理

package Sample_5;

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

public class Server {

    public static void transferOut(File file, OutputStream out) throws IOException {
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
            in.transferTo(out);
        } finally {
            Objects.requireNonNull(in).close();
        }
    }

    public static void handler(Socket socket) {
        new Thread(() -> {
            try(BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                OutputStream out = socket.getOutputStream()){
                String str;
                while ((str = reader.readLine()) != null) {
                    String[] params = str.split(" ");
                    String path = params[1].substring(1);
                    File file = new File(path);
                    if (file.exists()) {
                        out.write("HTTP/1.1 200 OK\r\n".getBytes());
                    } else {
                        file = new File("404.html");
                        out.write("HTTP/1.1 404 Not Found\r\n".getBytes());
                    }
                    out.write("content‐Type:text/html\r\n".getBytes());
                    out.write("\r\n".getBytes());
                    transferOut(file, out);
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(8080);) {
            while (true) {
                Socket socket = serverSocket.accept();
                if (socket != null)
                    handler(socket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
    try (ServerSocket serverSocket = new ServerSocket(8080);) {
        while (true) {
            Socket socket = serverSocket.accept();
            if (socket != null)
                handler(socket);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import java.io.*; import java.net.*; import java.util.*; import java.lang.*; public class Server extends ServerSocket { private static ArrayList User_List = new ArrayList(); private static ArrayList Threader = new ArrayList(); private static LinkedList Message_Array = new LinkedList(); private static int Thread_Counter = 0; private static boolean isClear = true; protected static final int SERVER_PORT = 10000; protected FileOutputStream LOG_FILE = new FileOutputStream( "d:/connect.log", true); public Server() throws FileNotFoundException, IOException { super(SERVER_PORT); // append connection log // Calendar now = Calendar.getInstance(); // String str = "[" + now.getTime().toString() + // "] Accepted a connection"; // byte[] tmp = str.getBytes(); // LOG_FILE.write(tmp); try { Socket socket = accept(); while (true) { new ServerReaderThread(socket); new ServerWriterThread(socket); } } finally { close(); } } public static void main(String[] args) throws IOException { new Server(); } // --- CreateServerThread class ServerReaderThread extends Thread { private Socket client; private BufferedReader in; private PrintWriter out; private String Username; public ServerReaderThread(Socket s) throws IOException { client = s; in = new BufferedReader(new InputStreamReader(client .getInputStream())); out = new PrintWriter(client.getOutputStream(), true); start(); } public void run() { try { int flag = 0; Thread_Counter++; String line = in.readLine(); while (!line.equals("bye")) { out.println(line); line = in.readLine(); } out.println("--- See you, bye! ---"); // System.out.println("--- See you, bye! ---"); client.close(); } catch (IOException e) { } finally { try { client.close(); } catch (IOException e) { } Thread_Counter--; } } } // --- CreateServerThread class ServerWriterThread extends Thread { priva
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值