本程序为在多线程条件下完成客户端与服务器进行的文字交互, 如果使用的是单线程, 那么当客户端发送第一条信息后, 在服务器端没有响应的情况下, socket套接字还在服务器那里, 则客户端无法继续发送信息, 对于服务器端亦然. 所以需要多线程操作.
1. 定义一个接收信息的线程
package com.gdzy.TCPmultipleThread;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ReceiveInfo implements Runnable {
private Socket soc1; //定义一个Socket类型的引用, 使其与调用这个线程的类建立起联系
//无参构造
public ReceiveInfo(){
}
//有参构造, 调用这个线程时将socket对象传入
public ReceiveInfo(Socket soc1){
this.soc1 = soc1;
}
//重写run方法
public void run(){
try {
//得到soc1 套接字引用的 输入流
InputStream is1 = soc1.getInputStream();
//将套接字的输入流, 封装到缓存流进行处理
BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
//循环读取缓存流中的数据并打印到控制台
while(true){
String info = br1.readLine();
System.out.println(info);
if(info.endsWith("bye") || info.endsWith("再见")|| info.endsWith("88")|| info.endsWith("拜拜")){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 创建一个发送信息的线程
package com.gdzy.TCPmultipleThread;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class SendInfo implements Runnable {
private Socket socSend; //定义一个Socket类型的引用, 使其与调用这个线程的类建立起联系
private String name; // 定义一个字符串变量,使其在发送信息时携带此名字, 在类调用这个线程时传入这个参数
//无参构造
public SendInfo(){
}
//有参构造, 调用这个线程时将socket对象传入
public SendInfo(Socket socSend, String name){
this.socSend = socSend;
this.name = name;
}
//重写run方法
public void run() {
try {
Scanner input = new Scanner(System.in); //创建一个键盘输入流
OutputStream os1 = socSend.getOutputStream(); // 得到套接字socket 引用socSend的输出流
//打印流, 此打印流的输出方向为 套接字socSend的输出流,即 将后面打印流中的信息 封装到, socSend的输出流中
PrintWriter pw1 = new PrintWriter(os1,true); // boolean 变量;如果为 true,则 println、printf 或 format 方法将刷新输出缓冲区
//循环键盘输入, 并将输入信息封装到套接字的输出流中发送
while(true){
String info = input.next();
pw1.println(name+":"+info); //此处不可以用print, 因为print 不具有刷新的功用, println具有自动刷新的功能
//当info中含有如下字符时, 跳出循环
if(info.endsWith("bye") || info.endsWith("再见")|| info.endsWith("88")|| info.endsWith("拜拜")){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 服务器端
package com.gdzy.TCPmultipleThread;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server1 {
/**
* 多线程下的 服务器端
*/
public static void main(String[] args) throws Exception {
ServerSocket ss1 = new ServerSocket(1237); //创建绑定到特定端口的服务器套接字。
Socket socServer = ss1.accept(); //侦听并接受到此套接字的连接。
Thread t1 = new Thread(new ReceiveInfo(socServer)); //创建接收消息线程
Thread t2 = new Thread(new SendInfo(socServer,"服务器")); //创建发送消息线程
t1.start(); //接收线程准备就绪
t2.start(); //发送线程准备就绪
}
}
4. 客户端
package com.gdzy.TCPmultipleThread;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client1 {
/**
* 多线程中的 客户端1 *
*/
public static void main(String[] args) throws Exception {
//创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket socClient = new Socket(InetAddress.getLocalHost(),1237);
//创建接收线程
Thread t1 = new Thread(new ReceiveInfo(socClient));
//创建发送线程
Thread t2 = new Thread(new SendInfo(socClient,"客户端"));
//使线程处于就绪状态
t1.start();
t2.start();
}
}