文章目录

  • 1. 前言
  • 2. Socket类
  • 2.1 Socket的通信过程
  • 2.2 控制Socket连接
  • 2.3 设置Socket的选项
  • 3. ServerSocket类
  • 3.1 构造ServerSocket
  • 3.2 ServerSocket的常用方法
  • 3.3 设置ServerSocket选项
  • 4. 基于TCP的BIO通信
  • 4.1 实现功能
  • 4.2 程序源代码
  • 4.3 程序效果截图


1. 前言

基于传输层协议TCP的网络通信是可靠的、有序的、差错控制的。TCP是面向连接的、可靠的流服务协议。TCP协议中,只有实现连接的双发才可以进行通信,因此广播和多播不是基于TCP的。下面首先介绍一些Socket和ServerSocket。

服务器、客户端建立通信的过程如下:

java winsock 端口转发 java socket 端口_服务器

  1. 服务器网络接口的IP地址为10.1.1.1。服务器端应用程序在80端口运行服务,建立ServerSocket,并监听有无连接请求。
  2. 客户端必须知道服务器的IP地址或主机名、服务开放端口,客户端向服务器的服务Socket(10.1.1.1:80)发起请求。
  3. 服务器一旦接收该请求,就会在80端口上建立一个Socket,该Socket的本地地址为10.1.1.1:80,外部地址为客户端建立的Socket(client端IP地址,client端port)。客户端port通常由操作系统指定。
  4. 服务器每接收一个客户端的请求,就会在服务端口上建立一个Socket,这些Socket的本地地址相同,但外部地址各不相同。每个外部地址即各个客户端的本地Socket。这样服务器既可以监听服务请求,又可以和每个客户端进行独立的通信。
  5. 客户端通过本地Socket与服务器进行通信、读写信息。就像打电话一样,一个号码呼叫另一个号码,一旦被呼叫者接听,通信链路就建立起来了,双方就可以通话了。

2. Socket类

2.1 Socket的通信过程

java winsock 端口转发 java socket 端口_服务器_02

2.2 控制Socket连接

  1. 设置客户端的地址和端口
Socket socket = new Socket(InetAddress.getByName("10.1.1.1")),8888,InetAddress.getByName("10.1.1.100"),12345);
  1. 绑定本地地址
try{
	socket = new Socket();
	socket.bind(new InetSocketAddress("10.1.1.100", 12345));
	scoket.connect(new InetSocketAddress("10.1.1.1", 8888));
}catch(){
	
}
  1. 连接远程服务器

见上一步,使用connect()方法去连接远程服务器

  1. 设置Socket建立连接的超时时间
SocketAddress socketAddress = new InetSocketAddress("10.1.1.1", 8888);
//创建Socket
Socket socket = new Socket;
//连接超时时间设置为10s
socket.connect(socketAddress, 10000);
  1. 获取Socket的输入流和输出流
    服务端和客户端之间通信的Socket一旦连接,就会通过一对输入输出流进行通信。如同打电话时,一旦被叫放接听,双方通话的业务信道建立,就可以交流了。
    Socket的getInputStream()方法和getOutputStream()方法分别实现获得输入流和输出流。
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader br = new BufferedReader(new IntputStreamReader(socket.getInputStream()));
  1. 获取Socket的地址端口信息
    如果需要获得本地或者远程Socket的地址和端口信息,可以使用以下方法:
getInetAddress() //获得远程服务器的IP地址
getPort() //获得远程服务器的端口
getLocalAddress() //获得客户端本地的IP地址
getLocalPort() //获得客户端本地的端口
  1. 关闭Socket
    当客户端与服务器的通信结束时,应该关闭Socket,以释放Socket占用的包括InputStream和OutputStream在内的各种资源。
if(!s.isClosed()) s.close();
//判断如果不是空的话则关闭
  1. Socket的状态
    Socket类提供了3中方法(isBound 、isClosed 和 isConnected)来判断Socket的状态。
    isBound方法用来判断Socket的绑定状态,只要曾经绑定过,即使Socket已经关闭,仍然返回true。可以理解为本地是否曾经建立过到远程主机的Socket连接。
    isClosed方法用来判断Socket是否已经关闭。
    isConnected方法用来判断Socket的连接状态。和isBound方法一样,即使Socket已经关闭,仍然返回true,isConnected的状态不清楚。可以理解为到远程主机的Socket是否曾经连接过。

2.3 设置Socket的选项

字段

含义

TCP_NODELAY

表示不要延迟和缓存

SO_LINGER

表示当执行Socket的close方法时,是否立即关闭底层的socket

SO_REUSEADDR

表示是否允许绑定Socket,即使之前的连接处于timeout状态

SO_SNDBUF

表示输出数据的缓冲区的大小

SO_RCVBUF

表示接收数据的缓冲区的大小

SO_KEEPALIVE

用来设置长时间处于空闲状态的Socket

SO_OOBINLINE

表示是否支持发送一字节的TCP紧急数据

SO_TIMEOUT

表示Socket的输入流等待数据的超时时间

3. ServerSocket类

3.1 构造ServerSocket

  • ServerSocket() throws IOException : 创建未绑定的服务器套接字
  • ServerSocket(int port) throws IOException:创建绑定到特定端口的服务器套接字
  • ServerSocket(int port, int backlog) throws IOException :创建绑定到特定端口的服务器套接字,并且连接请求队列的长度由backlog来设置
  • ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException:创建绑定到特定端口的服务器套接字,并且连接请求队列的长度由backlog来设置,指定本地要绑定的IP地址bindAddr,一台服务器可能有多个网络接口,如果设置了bindAddr,则服务器只接收到该地址的连接请求。

3.2 ServerSocket的常用方法

  1. 绑定端口
ServerSocket serverSocket = new ServerSocket(8000);

ServerSocket 对象创建时,如果使用无参构造方法,通常之后会做一些选项的设置,再来进行端口的绑定,则需要使用bind方法。

  1. 设置连接请求队列的长度
int queue = 100;
serverSocket.bind(s, queue);

若没有设置,由操作系统指定

  1. 获取ServerSocket的地址端口信息
  • getInetAddress() : 获得服务器的本地IP地址
  • getLocalPort() : 获得服务器本地监听端口
  • getLocalSocketAddress() : 获得服务器的本地IP地址和监听端口
  1. 关闭ServerSocket
    ServerSocket 建立之后,可以显示地关闭它。同Socket一样,使用close方法关闭,当关闭时,如果队列中有尚未accept的客户端请求,则会触发SocketException。
  2. 接收客户端的连接请求
    ServerSocket的accept方法监听客户端的连接请求并且接收请求。
  3. ServerSocket的状态
    ServerSocket 类提供了两种方法(isBound、isClosed)来判断Socket的状态。具体可参考Socket类的相应方法。

3.3 设置ServerSocket选项

字段

含义

SO_TIMEOUT

表示等待客户端连接的超时时间

SO_REUSEADDR

表示是否允许重复绑定Socket处于timeout状态的端口

SO_RCVBUF

设置接收数据的缓冲区的大小

4. 基于TCP的BIO通信

4.1 实现功能

服务器创建服务并监听端口,与客户端建立连接,并向客户端转发消息,客户端输入quit命令即可退出。

4.2 程序源代码

Server类:

package com.company.bio;

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

public class Server {
    public static void main(String[] args){
        final  String QUIT = "quit";
        final int DEFAULT_PORT = 8888;
        ServerSocket serverSocket = null;

        //绑定监听端口
        try{
            serverSocket = new ServerSocket(DEFAULT_PORT);
            System.out.println("启动服务器,监听端口"+DEFAULT_PORT);

            while (true){
                // 等待客户端连接
                Socket socket = serverSocket.accept();
                System.out.println("客户端[" + socket.getPort() + "]已连接");
                //创建IO流
                BufferedReader bufferedReader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream())
                );
                BufferedWriter bufferedWriter = new BufferedWriter(
                        new OutputStreamWriter(socket.getOutputStream())
                );

                String msg = null;
                while ((msg = bufferedReader.readLine()) != null) {
                    // 读取客户端发送的消息,当对方关闭时返回null

                    System.out.println("客户端["+socket.getPort()+"]:"+ msg);
                    //回复客户发送的消息
                    bufferedWriter.write("服务器:" + msg + "\n");
                    bufferedWriter.flush(); //保证缓冲区的数据发送出去
                    //查看客户端是否退出
                    if(QUIT.equals(msg)){
                        System.out.println("客户端["+socket.getPort()+"]已退出");
                        break;
                    }
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (serverSocket != null){
                try {
                    serverSocket.close();
                    System.out.println("关闭ServerSocket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Client类:

package com.company.bio;

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

public class Client {
    public static void main(String[] args)  {
        final  String QUIT = "quit";
        final String DEFAULT_SERVER_HOST = "127.0.0.1";
        final int DEFAULT_SERVER_PORT = 8888;
        Socket socket = null;
        BufferedWriter bufferedWriter = null;

        try{
            // 创建socket
            socket = new Socket(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
            //创建IO流
            BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(socket.getInputStream())
            );
            bufferedWriter = new BufferedWriter(
                 new OutputStreamWriter(socket.getOutputStream())
            );
            //等待用户输入信息
            BufferedReader consolReader = new BufferedReader(
                    new InputStreamReader(System.in)
            );
            while (true) {
                String input = consolReader.readLine();
                //发送消息给服务器
                bufferedWriter.write(input + "\n");
                bufferedWriter.flush();
                //读取服务器返回的消息
                String msg = bufferedReader.readLine();
                System.out.println(msg);

                //查看用户是否退出
                if(QUIT.equals(input))break;
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if(bufferedWriter != null){
                try {
                    bufferedWriter.close();
                    System.out.println("关闭socket");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.3 程序效果截图

Client:

java winsock 端口转发 java socket 端口_客户端_03


Server:

java winsock 端口转发 java socket 端口_IP_04