Java中Socket编程

目录

一、什么是Http协议:

 二、Http协议请求格式

三、IP地址和端口号

四、InetAddress类详解

1. getByName(String host):通过主机名或IP地址字符串创建一个InetAddress对象。

2. getHostName():获取主机名。   

3. getHostAddress():获取IP地址的字符串表示形式。   

4. isReachable(int timeout):检查主机是否可达。可以指定超时时间(以毫秒为单位)来限制等待时间。

5. getCanonicalHostName():获取完全限定的主机名。

五、TCP/UDP

TCP:

UDP:

TCP三次握手:

TCP四次挥手:

案例:

创建TcpServer类:

六、Tcp协议使用案例

Tcp协议循环接收发送数据:

基于Tcp协议手写出Http服务器


一、什么是Http协议:

HTTP(Hypertext Transfer Protocol)是一种用于在客户端和服务器之间传输数据的协议。它是现代web通信的基础,用于在客户端(如浏览器)和服务器之间传递HTML、图片、视频、文件等数据。

HTTP协议的特点包括:

1. 简单易读:HTTP协议使用简单的文本格式,易于阅读和理解。

2. 基于请求-响应模型:客户端发送一个HTTP请求给服务器,服务器则返回一个HTTP响应。请求和响应之间包括头部和消息主体。

3. 无状态:HTTP协议是无状态的,每个请求都是独立的,服务器不会保留之前请求的状态信息。为了保持状态,可以使用会话(Session)、Cookie等机制。

4. 可靠性较低:HTTP协议是基于TCP/IP协议栈的应用层协议,本身没有提供可靠性机制。但可以通过其他机制(如重传机制)来提高可靠性。

5. 支持缓存:HTTP协议支持客户端和服务器之间的缓存,可以减少数据传输和提高性能。

6. 支持多媒体:HTTP协议不仅支持传输文本数据,也支持传输二进制数据(如图片、音频、视频等)。

7. 可扩展性:HTTP协议支持扩展,可以定义新的请求方法、头部字段等。

HTTP协议使用请求方法(GET、POST、PUT、DELETE等)来定义客户端向服务器发送请求的方式。常见的HTTP版本有HTTP/1.0、HTTP/1.1和HTTP/2。每个版本在性能、功能和安全性方面有所不同。

总之,HTTP协议是现代web通信的基石,它使得客户端和服务器之间的数据交换变得简单和可靠。如果您有更多关于HTTP协议的问题,请随时提问。

 二、Http协议请求格式

HTTP(Hypertext Transfer Protocol)是用于在Web浏览器和Web服务器之间传输数据的协议。HTTP请求是客户端向服务器发送请求的格式。以下是HTTP请求的基本格式:
 

<HTTP Method> <Request URL> <HTTP Version>
<Headers>

<Request Body>
```

其中,各部分的含义如下:

1. HTTP Method(HTTP方法):表示客户端要执行的操作类型。常见的HTTP方法有:
   - GET:请求获取指定资源。
   - POST:在服务器上创建新的资源。
   - PUT:替换服务器上指定的资源。
   - DELETE:删除服务器上的指定资源。
   - HEAD:获取资源的元信息。
   - OPTIONS:获取服务器支持的HTTP方法列表。

2. Request URL(请求URL):表示客户端想要访问的资源的URL地址。

3. HTTP Version(HTTP版本):表示客户端使用的HTTP协议版本,常见的有HTTP/1.1和HTTP/2。

4. Headers(请求头):包含了请求相关的各种头部信息,以键值对的形式表示,常见的头部信息有:
   - Host:指定被请求资源的主机名称。
   - User-Agent:标识客户端的用户代理,通常是浏览器名称和版本号。
   - Content-Type:指定请求体的数据类型(如application/json)。
   - Authorization:用于身份验证的凭据。

5. Request Body(请求体):可选项,包含了请求的数据体。在一些HTTP方法(比如POST和PUT)中,请求体用于传输客户端向服务器发送的数据。

以下是一个示例的HTTP请求格式的实例:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36
```

这是一个GET请求,请求访问www.example.com域名下的index.html页面,使用HTTP/1.1协议版本。请求头中包含了Host和User-Agent等信息。

注意:请求体是可选的,在GET请求中通常不包含请求体。而在POST请求中,请求体通常包含了需要传递给服务器的数据。

三、IP地址和端口号

IP地址和端口号是计算机网络中用于标识和定位网络中的设备和应用程序的重要参数。

IP地址(Internet Protocol Address)是一个由32位或128位二进制数组成的数字,用于唯一标识网络中的设备,如计算机、路由器或服务器。IP地址的作用是在网络中寻址和定位设备,并提供网络互联。

IPv4是目前广泛使用的IP地址版本,它由四组由点分隔的八位十进制数组成,如192.168.0.1。IPv6是下一代IP地址版本,由八组由冒号分隔的十六位十六进制数构成,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。

端口号是网络通信中的一种逻辑概念,它用于区分和定位一台计算机上的不同应用程序或服务。端口号是一个16位的整数,范围从0到65535。其中,0到1023的端口号被称为已知端口号,用于常见的网络服务和协议,如HTTP的端口号是80,HTTPS的端口号是443。1024到49151的端口号是注册端口号,用于被软件或系统管理员分配给特定应用程序。49152到65535的端口号是动态或私有端口号,用于临时分配给客户端应用程序。

在网络通信过程中,当数据从源设备发送到目标设备时,需要使用源IP地址和端口号以及目标IP地址和端口号来建立网络连接并传输数据。通过组合IP地址和端口号,可以实现不同设备和应用程序之间的准确定位和通信。

四、InetAddress类详解

InetAddress类是Java中用于表示IP地址的类。它提供了一些方法来获取和操作IP地址的信息。

以下是InetAddress类的一些常用方法:

1. getByName(String host):通过主机名或IP地址字符串创建一个InetAddress对象。

InetAddress address = InetAddress.getByName("www.example.com");
   ```

2. getHostName():获取主机名。
   

String hostname = address.getHostName();
   ```

3. getHostAddress():获取IP地址的字符串表示形式。
   

String ipAddress = address.getHostAddress();
   ```

4. isReachable(int timeout):检查主机是否可达。可以指定超时时间(以毫秒为单位)来限制等待时间。

boolean reachable = address.isReachable(3000); // 3秒超时
   ```

5. getCanonicalHostName():获取完全限定的主机名。

 String canonicalHostname = address.getCanonicalHostName();
   ```

InetAddress类是一个抽象类,有两个常用的子类:
- Inet4Address:表示IPv4地址。
- Inet6Address:表示IPv6地址。下面是一个示例代码,演示了如何使用InetAddress类:

import java.net.InetAddress;
import java.net.UnknownHostException;

public class InetAddressExample {
    public static void main(String[] args) {
        try {
            InetAddress address = InetAddress.getByName("www.example.com");
            System.out.println("Host Name: " + address.getHostName());
            System.out.println("IP Address: " + address.getHostAddress());

            boolean reachable = address.isReachable(3000);
            System.out.println("Is Reachable: " + reachable);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}
```

这段代码通过主机名创建了一个InetAddress对象,然后获取了主机名和IP地址,并检查了主机是否可达。

运行结果:

五、TCP/UDP

TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常见的传输层协议,用于在计算机网络中传输数据。

以下是TCP和UDP的基本特点和区别:

TCP:

- 三次握手,四次挥手
- 面向连接:在通信之前,发送端和接收端需要先建立连接,然后再进行数据传输。连接的建立和断开过程需要消耗时间和资源。
- 可靠性:TCP提供可靠的数据传输,它使用序列号、确认应答和重传机制来确保数据的可靠性。如果数据包丢失或损坏,TCP会重新发送这些数据包。
- 有序性:TCP保证数据包按照发送的顺序到达接收端,因此接收端可以按照正确的顺序进行数据的处理。
- 流量控制和拥塞控制:TCP使用滑动窗口和拥塞避免等机制来控制数据传输的速度,以避免过多的数据拥塞网络,影响整体性能。

Java中UDP模拟客户端接收数据:

package lzx7;

import java.io.IOException;
import java.net.*;

public class UdpClient {
    public static void main(String[] args) throws IOException {

        DatagramSocket datagramSocket = new DatagramSocket();
        byte[] bytes = "我爱你".getBytes();

        InetAddress inetAddress = InetAddress.getByName("mayikt.server.com");
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,inetAddress,8080);
        datagramSocket.send(datagramPacket);
        datagramSocket.close();
    }
}

Java中UDP模拟服务器发送数据:

package lzx7;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpServer {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket(8080);

        byte[] bytes = new byte[1024];

        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
        //开始接收,若没有数据,则会一直阻塞等待
        System.out.println("开始接收数据");
        datagramSocket.receive(datagramPacket);
        System.out.println("接收到数据");
        byte[] data = datagramPacket.getData();
        System.out.println(new String(data));
        datagramSocket.close();
    }
}

UDP:


- 无连接:UDP不需要在通信之前建立连接,直接发送数据。因此UDP的传输速度相对较快。
- 不可靠性:UDP不提供可靠性的传输,它不保证数据的可靠性、有序性和错误检测。如果数据包丢失或损坏,UDP不会重新发送数据。
- 简单性和轻量级:UDP的头部信息较简单,没有TCP的复杂机制,因此使得UDP的开销较小,传输效率高。
- 实时性:由于UDP不需要等待连接的建立,适用于实时性较高的应用,如音频、视频等。

TCP和UDP的选择取决于具体应用场景和需求。如果需要可靠的数据传输和有序性,可以选择TCP。而对于实时性要求较高的应用,可以选择UDP来降低延迟并提高性能。

TCP三次握手:

TCP三次握手是建立可靠的传输连接所使用的一种协议。它主要用于确认双方的通信能力和同步双方的初始序列号。

下面是TCP三次握手的详细过程:

1. 客户端发送SYN请求:当客户端希望与服务器建立连接时,它会发送一个带有SYN(同步)标志的数据包给服务器,并选择一个初始序列号(随机产生的一个值)作为数据包的一部分。

2. 服务器回复SYN-ACK:当服务器接收到客户端的SYN请求后,它会回复一个带有SYN和ACK(确认)标志的数据包给客户端。服务器也会选择一个初始序列号,并将客户端的初始序列号加上一。

3. 客户端发送ACK:当客户端接收到服务器的SYN-ACK回复后,它会发送一个带有ACK标志的数据包给服务器,确认收到了服务器的回复。这个ACK包的序列号是服务器发送的SYN请求的初始序列号加上一。

完成了这个三次握手过程之后,TCP连接就建立起来了,双方可以开始进行数据的传输。这样做的目的是为了确保通信双方都能接收、发送数据,并且双方都知道彼此的初始序列号。当连接建立完成后,数据的传输就可以开始了。

需要注意的是,三次握手过程中每个步骤都需要收到对方的确认,确保连接的可靠性。此外,如果在握手过程中有任何一步失败或超时,整个过程会重新开始,直到连接成功建立或放弃连接。这确保了TCP连接的稳定性和可靠性。

TCP四次挥手:

TCP四次挥手是断开TCP连接的过程,它用于双方终止连接,并确保双方都能安全关闭连接。

下面是TCP四次挥手的详细过程:

1. 客户端发送FIN:当客户端希望关闭连接时,它会发送一个带有FIN(结束)标志的数据包给服务器。这表示客户端不再发送数据了,但仍然可以接收数据。

2. 服务器回复ACK:当服务器接收到客户端的FIN请求后,它会发送一个带有ACK(确认)标志的数据包给客户端,表示已经接收到了客户端的结束请求。

此时,服务器已经发送了确认信号,但仍然可以继续向客户端发送数据。

3. 服务器发送FIN:当服务器也准备关闭连接时,它会发送一个带有FIN标志的数据包给客户端,表示服务器不再发送数据了。

4. 客户端回复ACK:当客户端接收到服务器的FIN请求后,它会发送一个带有ACK标志的数据包给服务器,表示已经接收到了服务器的结束请求。

完成了这个四次挥手过程之后,TCP连接就正式关闭了,双方不再进行数据的传输。

需要注意的是,在完成四次挥手过程后,双方的连接仍然处于TIME_WAIT状态,等待2倍的最大报文段生存时间(MSL)后才会真正关闭连接。这是为了确保所有的报文都被完整地传输,避免之前的报文在网络中滞留导致的问题。

通过四次挥手过程,TCP连接能够被可靠地关闭,确保数据的安全性和可靠性。

案例:

package lzx7;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getByName("mayikt.server.com"), 8080);
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("我爱你".getBytes());
        outputStream.close();
        socket.close();
    }
}

程序运行结果为报错

Exception in thread "main" java.net.ConnectException: Connection refused: connect

因为TCP链接需要三次握手的过程,所以需要先配置好服务端(也即接收信息的一端)

创建TcpServer类:

package lzx7;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args)throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        Socket socket = serverSocket.accept();//监听客户端发送数据给服务器端,如果客户端没有发送数据,则会阻塞
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = inputStream.read(bytes);
        System.out.println("接收到以下数据:\n"+new  String(bytes,0,len));
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

将该Server启动后,它正在等待Client发送信息

运行上面的Client发送信息后,Server成功接收到信息并打印:

六、Tcp协议使用案例

Tcp协议循环接收发送数据:

TcpServer:

package lzx8;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8081);

        while(true){
            new Thread(() -> {
                try {
                    Socket socket = serverSocket.accept();
                    InputStream inputStream = socket.getInputStream();
                    byte[] bytes = new byte[1024];
                    int len = inputStream.read(bytes);
                    System.out.println("服务器接收到以下数据:"+new String(bytes,0,len));
                    OutputStream outputStream = socket.getOutputStream();
                    outputStream.write(("我收到了"+ UUID.randomUUID()).getBytes());
                    inputStream.close();
                    socket.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

TcpClient:

package lzx8;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;

public class TcpClient {
    public static void main(String[] args)throws IOException {
        Scanner sc = new Scanner(System.in);
        while(true){
            Socket socket = new Socket("127.0.0.1", 8081);
            System.out.println("请输入要发送给服务器的数据:");
            String s = sc.nextLine();
            if("666".equals(s)){
                break;
            }
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(s.getBytes());
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println("客户端收到服务器响应的数据:"+new String(bytes,0,len));
            outputStream.close();
            socket.close();
        }
        sc.close();
    }
}

 

TcpServer的代码没有考虑线程的管理,改进方案:使用线程池管理

package lzx8;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8081);
        ExecutorService executorService = Executors.newFixedThreadPool(10); // 使用大小为10的线程池

        while (true) {
            Socket socket = serverSocket.accept();
            executorService.execute(() -> {
                try {
                    handleConnection(socket);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    private static void handleConnection(Socket socket) throws IOException {
        try (socket) {
            InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len = inputStream.read(bytes);
            System.out.println("服务器接收到以下数据:" + new String(bytes, 0, len));
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(("我收到了" + UUID.randomUUID()).getBytes());
            inputStream.close();
            outputStream.close();
        }
    }
}

成功实现。 

基于Tcp协议手写出Http服务器:

package Study_Http;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class HttpTcpServer {
    public static void main(String[] args) throws IOException {
        /**
         * 创建ServerSocket  创建一个可以装下10个线程的线程池
         */
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ServerSocket serverSocket = new ServerSocket(80);
        while (true) {
            Socket socket = serverSocket.accept();
            executorService.execute(() -> {
                try{
                    handleConnection(socket);
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        }
    }

    public static void handleConnection(Socket socket) throws IOException {
        OutputStream outputStream = socket.getOutputStream();
        File file = new File("D:\\sjava\\test.html");
        FileInputStream fileInputStream = new FileInputStream(file);
        int len;
        byte[] bytes = new byte[1024];
        while((len = fileInputStream.read(bytes))!=-1){
            outputStream.write(bytes,0,len);
        }
        fileInputStream.close();
        outputStream.close();
    }
}

编写的HTML文件如下:

<html>
<title>这是一个测试</title>
<div>
    <h1>lzx</h1>
    <h1>yy</h1>
</div>

</html>

成功通过浏览器访问到

 

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Java Socket编程是一种网络编程模型,它是建立在TCP/IP协议上的,允许在网络上进行数据通信。Java Socket编程提供了一种方便的方法,可以通过网络连接来传输数据。 以下是Java Socket编程的一些基本步骤: 1. 创建一个ServerSocket对象,指定端口号,并监听客户端请求。 2. 当客户端请求连接时,ServerSocket会接收请求,并创建一个Socket对象,用于与客户端进行通信。 3. 使用Socket对象的输入输出流来进行数据传输。 4. 客户端与服务端通信完成后,关闭Socket对象和ServerSocket对象。 下面是一个简单的Java Socket编程示例,用于创建一个简单的服务端: ```java import java.net.*; import java.io.*; public class ServerSocketDemo { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(8088); System.out.println("Server started."); while (true) { Socket socket = serverSocket.accept(); System.out.println("Client connected."); // 从客户端读取数据 InputStream inputStream = socket.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String data = reader.readLine(); System.out.println("Received data from client: " + data); // 向客户端发送数据 OutputStream outputStream = socket.getOutputStream(); PrintWriter writer = new PrintWriter(outputStream); writer.println("Hello, client!"); writer.flush(); // 关闭Socket对象 socket.close(); } } catch (IOException e) { System.out.println("Error: " + e.getMessage()); } finally { if (serverSocket != null) { serverSocket.close(); } } } } ``` 在上面的示例,我们创建了一个ServerSocket对象,并指定端口号为8088。然后,我们使用while循环来监听客户端请求。当有客户端请求连接时,我们使用serverSocket.accept()方法来接收请求,并创建一个Socket对象,用于与客户端通信。接着,我们使用Socket对象的输入输出流来进行数据传输。最后,我们关闭Socket对象和ServerSocket对象。 这只是一个简单的示例,Java Socket编程还有很多其他用法和技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jay/.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值