JAVA——基于TCP协议的文本聊天程序
基于TCP协议的文本聊天程序
1.要求
使用JAVA完成一个基于TCP协议的文本聊天程序,建立TCP客户端套接字,分别为服务端和客户端,并使两端可以互联。
2.TCP协议
在TCP协议中,有服务器端(Server端)和客户端(Client端)的概念。TCP传输数据的过程是:服务器端程序在本机的某个端口上监听,等待客户端连接到此端口,一旦客户端连接到了此端口,服务器端和客户端就可以进行数据传输了,数据传输结束后,关闭连接。下图为TCP数据传输过程。
3.语法
在JAVA类库中,与Socket相关的类都在java.net这个包中。InetAddress是代表IP地址的类,ServerSocket就是代表服务器端的类,Socket是代表客户端的类,同时也可以看作是代表连接的类,以下将对这三个类做一个简单的介绍:
- InetAddress类
InetAddress类的构造方法是私有的,所以我们不能够通过new关键字来创建一个InetAddress类的实例。但是InetAddress类有几个静态方法,可以用于生成InetAddress的实例。我们在此处用到的就是getByAddress(byte[])这个静态的方法。这个方法的参数是一个byte数组。我们知道,一个IP地址是由4个字节组成的,只要将这4个字节顺序放在一个byte数组中,然后将它作为参数传递给这个方法,就可以得到一个IP地址。 - ServerSocket类
ServerSocket类代表了服务器端,根据图19-5,它有以下几个方法。
1)构造方法ServerSocket(int):参数是一个端口号,ServerSocket的实例创建出来后,会在指定的端口监听。
2)accept():在指定的端口等待客户端连接过来。在客户端连接过来之前,当前线程是处于挂起状态的,也就是说,如果没有客户端连接过来,这个方法不会执行结束。这个方法返回值是一个Socket类型的实例。
3)close():结束监听,释放资源。 - Socket类
Socket类可以看做是两个端口之间的连接。两台电脑之间的通信使用的就是这个类。有两种途径来创建Socket类的实例。一种是前面说的,ServerSocket类的accpet()方法会返回一个Socket的实例;另一种是通过Socket类的构造方法创建。
这时候是通过Socket进行数据传输的,Socket类中主要的方法有以下几个:
1) 构造方法Socket(InetAddress,int):指定Socket需要连接的远程IP地址和端口。使用此构造方法创建出Socket实例,会连接到这个IP地址:端口号,并进一步可以使用这个IP地址:端口号进行数据传输。
2) getInputStream():方法返回值是一个InputStream的实例,通过输入流可以读取信息。必须在建立连接后才能调用此方法。
3) getOutputStream():方法的返回值是一个OutputStream的实例,通过输出流可以向远程计算机输入信息。同样,也必须在建立连接后才能调用此方法。
4) close():关闭连接,释放资源。
4.服务端
- 创建套接字描述符(socket)
- 设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
- 将套接字描述符绑定到服务器地址(bind)
- 将套接字描述符设置为监听套接字描述符(listen),等待来自客户端的连接请求,监听套接字维护未完成连接队列和已完成连接队列
- 从已完成连接队列中取得队首项,返回新的已连接套接字描述符(accept),如果已完成连接队列为空,则会阻塞
- 从已连接套接字描述符读取来自客户端的请求(read)
- 向已连接套接字描述符写入应答(write)
- 关闭已连接套接字描述符(close),回到第5步等待下一个客户端的连接请求
代码:
import java.io.BufferedReader; // 引入需要使用的类
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class rw24 {
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
ServerSocket ss = new ServerSocket(7777);
// (1)在本机的7777端口监听
System.out.println("服务器端在7777端口监听……");
Socket s = ss.accept(); // (2)等待客户端连接到7777端口
// 如果没有客户端连接过来,accept()方法不会返回
System.out.println("已有客户端连接过来,开始进行通信……");
InputStream in = s.getInputStream(); // (3)得到输入流
OutputStream out = s.getOutputStream();// (4)得到输出流
PrintWriter pw = new PrintWriter(out);
// 使用PrintWriter向客户端输出数据
System.out.println("正在向客户端发送消息……");
pw.write("客户端,你好,这里是服务器端。\r\n");
pw.flush(); // 强制发送数据
System.out.println("向客户端发送消息完成。");
// 使用BufferedReader读取客户端发送来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String response = br.readLine();
System.out.println("\n======下面是客户端发来的消息======");
System.out.println(response);
System.out.println("================================");
br.close(); // (5)调用close()方法释放资源
pw.close();
s.close();
ss.close();
} catch (UnknownHostException e) {
System.out.println("无法找到相应的机器,错误信息。" + e.getMessage());
} catch (IOException e) {
System.out.println("数据传输出现异常:" + e.getMessage());
}
}
}
编译结果如下图所示:
5.客户端
客户端:
- 创建套接字描述符(socket)
- 设置服务器的IP地址和端口号(需要转换为网络字节序的格式)
- 请求建立到服务器的TCP连接并阻塞,直到连接成功建立(connect)
- 向套接字描述符写入请求(write)
- 从套接字描述符读取来自服务器的应答(read)
- 关闭套接字描述符(close)
urce=copy
代码:
import java.io.BufferedReader; // 引入需要使用的类
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class rw25 {
public static void main(String[] args) {
// TODO Auto-generated method stub
byte[] addr = new byte[] { 127, 0, 0, 1 }; // 与本机进行通信
try {
InetAddress local = InetAddress.getByAddress(addr);
Socket s = new Socket(local, 7777);
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String response = br.readLine();
System.out.println("======下面是服务端发来的消息======");
System.out.println(response);
System.out.println("================================");
System.out.println("正在向服务器端发送消息……");
PrintWriter pw = new PrintWriter(out);
pw.write("服务器你好,这里是客户端。");
pw.flush();
pw.close();
br.close();
s.close();
System.out.println("向服务器端发送消息结束。");
} catch (UnknownHostException e) {
System.out.println("无法找到相应的机器,错误信息。" + e.getMessage());
} catch (IOException e) {
System.out.println("数据传输出现异常:" + e.getMessage());
}
}
}
6.文本聊天程序
- 整个通信的过程如下:
1) 服务器监听7777端口。
2) 客户端连接服务器端的7777端口。
3) 服务器端向客户端发送数据。
4) 客户端收到数据,输出到控制台。
5) 客户端向服务器端发送数据。
6) 服务器端收到数据并输出到控制台。
7) 服务器端和客户端关闭连接,释放资源。
服务器:
客户端: