TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。基于TCP网络通讯实现的类主要有服务器端的ServerSocket用客户端的Socket。TCP类似于电话系统,建立双向的通信通道,确定连接,话音顺序接听。
Socket是客户端的通信套接字,可指定远端IP地址、端口进行连接通信,也可以通过方法获取已连接的socket的远端IP地址、端口,以及将此socket以字节输入流和输出流的形式返回,当与数据输入流和输出流绑定,便可实现客户端的网络通信。
构造函数均为public修饰类型,如果创建socket时发生I/O错误,均抛出IOException异常。常用构造函数如下:
Socket(InetAddress address,int port):创建一个socket并与规定的IP地址的指定的端口相连接。 Socket(String host,int port):创建一个socket并与指定主机的指定端口连接。
ServerSocket是服务器的通讯套接字,用来侦听客户端请求的连接,并为每个新连接创建一个socket对象,由此创建绑定此socket的输入流和输出流,与客户端实现网络通信。
构造函数均为public修饰类型,如果创建socket时发生I/O错误,均抛出IOException异常。常用构造函数如下:
ServerSocket(int port):在所给定的用来侦听的端口上建立一个服务器套接字。
TCP通信的通信流程:
打开服务器,等待客户端连接–>客户端连接上服务器–>数据通讯。
服务器端(Server.java)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.Scanner; //发送信息线程 class SendThreat implements Runnable { Socket socket; PrintWriter pWriter;//使用PrintWriter流来向客户端发送信息 private ArrayList<Socket> socketList;//接收来自主线程的客户端集合 Scanner scanner = new Scanner(System.in);//从键盘输入获取信息 public SendThreat(Socket socket,ArrayList<Socket> socketList) { super(); this.socket = socket; this.socketList=socketList; try { //接收socket的字节输出流,用OutputStreamWriter把字节输出流转化为字符流,再传给PrintWriter pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (true) { String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine();//获取从键盘输入的信息 if (strMsg == "b") { break; } //把服务器收到的信息转发给各个客户端 for (Socket clientSock : socketList) { PrintWriter pWriter; try { pWriter = new PrintWriter(clientSock.getOutputStream());//获取socket的输出流,用来向客户端发送信息 pWriter.println(strMsg);//输出信息给客户端 pWriter.flush();//刷新输出流 } catch (IOException e) { e.printStackTrace(); } } } } } //接收信息线程 class ReceiveThreat implements Runnable { Socket socket; BufferedReader bReader; private ArrayList<Socket> socketList; public ReceiveThreat(Socket socket, ArrayList<Socket> socketList) { super(); this.socket = socket; this.socketList = socketList; try { bReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));//获取socket的输入流 } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (true) { try { String strMsg = bReader.readLine(); System.out.println(strMsg); for (Socket clientSock : socketList) { PrintWriter pWriter = new PrintWriter(clientSock.getOutputStream()); pWriter.println(strMsg); pWriter.flush(); } } catch (IOException e) { Server.socketList.remove(socket); } } } } public class Server { public static ArrayList<Socket> socketList = new ArrayList<>();//定义一个集合用来存放 监听到的客户端socket @SuppressWarnings("resource") public static void main(String[] args) { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(30000);//新建一个服务端ServerSocket,端口号为30000 System.out.println("等待客户端连接!"); } catch (IOException e) { e.printStackTrace(); } while (true) { Socket socket = null; while (true) { try { socket = serverSocket.accept();//监听客户端的连接 socketList.add(socket);//加入集合 System.out.println("客户端 " + socket.getInetAddress().getHostAddress() + "连接成功!"); //为该客户端分别开启一个发送信息线程和接收信息线程 new Thread(new SendThreat(socket,socketList)).start(); new Thread(new ReceiveThreat(socket, socketList)).start(); } catch (IOException e) { e.printStackTrace(); } } } } }
客户端(Client.java)
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; //发送信息线程 class SendClientThreat implements Runnable { Socket socket; PrintWriter pWriter; Scanner scanner; public SendClientThreat(Socket socket) { super(); this.socket = socket; this.scanner = new Scanner(System.in); try { pWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { while (true) { String strMsg =socket.getInetAddress().getHostAddress()+":"+ scanner.nextLine(); pWriter.println(strMsg); pWriter.flush(); } } } //接收信息线程 class ReceiveClientThreat implements Runnable { Socket socket; BufferedReader bReader; public ReceiveClientThreat(Socket socket) { super(); this.socket = socket; try { bReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { try { while (true) { String strMsg = bReader.readLine(); System.out.println(strMsg); } } catch (IOException e) { e.printStackTrace(); } } } public class Client{ private static Socket socket; public static void main(String[] args) { String IPAdress="10.32.0.23";//服务端的IP try { socket = new Socket(IPAdress, 30000);//创建一个客户端socket,指定服务端的IP和端口号 System.out.println("连接主机成功! "); new Thread(new ReceiveClientThreat(socket)).start(); new Thread(new SendClientThreat(socket)).start(); } catch (IOException e) { e.printStackTrace(); } } }
服务器端:
客户端:
注意:
在调试的过程中,如果出现下图所示的错误时:
可以打开控制台,输入:netstat -ano
来查看现在端口被那个进程占用,找到pid
然后输入:taskkill /f /pid 7144 手动删除这个进程就可以了,其中7144是占用端口的进程号pid
https://blog.csdn.net/HD1099/article/details/47374679?utm_source=blogxgwz0