java TCP通信

java TCP通信

一、相关知识学习

  TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能。基于TCP网络通讯实现的类主要有服务器端的ServerSocket用客户端的Socket。TCP类似于电话系统,建立双向的通信通道,确定连接,话音顺序接听。

1、 Socket

  Socket是客户端的通信套接字,可指定远端IP地址、端口进行连接通信,也可以通过方法获取已连接的socket的远端IP地址、端口,以及将此socket以字节输入流和输出流的形式返回,当与数据输入流和输出流绑定,便可实现客户端的网络通信。
  构造函数均为public修饰类型,如果创建socket时发生I/O错误,均抛出IOException异常。常用构造函数如下:

Socket(InetAddress address,int port):创建一个socket并与规定的IP地址的指定的端口相连接。
Socket(String host,int port):创建一个socket并与指定主机的指定端口连接。

2、 ServerSocket

  ServerSocket是服务器的通讯套接字,用来侦听客户端请求的连接,并为每个新连接创建一个socket对象,由此创建绑定此socket的输入流和输出流,与客户端实现网络通信。
  构造函数均为public修饰类型,如果创建socket时发生I/O错误,均抛出IOException异常。常用构造函数如下:

ServerSocket(int port):在所给定的用来侦听的端口上建立一个服务器套接字。

3、TCP通信的通信流程

TCP通信的通信流程:
打开服务器,等待客户端连接–>客户端连接上服务器–>数据通讯。

二、训练

任务要求:
  • 编写两个java application应用程序,完成以下功能:
  • 一个程序为服务端,建立TCP服务端套接字。
  • 另外一个程序为客户端,建立TCP客户端套接字。
  • 这两个程序可以互联,完成一个基于TCP/IP网络的文本聊天程序。
java程序

服务器端(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