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.****.net/HD1099/article/details/47374679?utm_source=blogxgwz0