一.题目要求如下:
1.编写客户端程序,连接同一服务器,并实现发送线程和接收线程,发送的信息中应包括“消息+发送时间”,显示消息应包括:“Socket+信息+发送时间”。(GUI实现后续完成)
2.编写服务器端程序,使用TCP Socket启动12345端口,判断该端口是否被占用,可以接收多个客户端的连接(连接成功发出欢迎信息“Welcome”+客户端套接字信息),并将Socket保存在Vector中,将收到的信息转发给所有连接的客户端。(GUI实现后续完成)
二.具体代码实现案例
1.服务器端代码如下:(先有服务器再有客户机,故此处先给出服务器端程序)
package day26.tcp.test;
/*
* 编写服务器端程序,使用TCP Socket启动12345端口,判断该端口是否被占用,可以接收多个客户端的连接
* (连接成功发出欢迎信息“Welcome”+客户端套接字信息),并将Socket保存在Vector中,将收到的信息转
* 发给所有连接的客户端
* */
import java.net.*;
import java.io.*;
public class ServerTest {
public static void main(String[] args) throws IOException{
System.out.println("服务器等待建立连接..."); //服务器端给出提示信息
//创建服务器Socket对象
while(true){
try (ServerSocket ss = new ServerSocket(12345)) { //判断端口12345是否被占用
Socket s = ss.accept(); //监听客户端Socket对象
int count = 1; //记录客户机数量并标记,第一个客户机标记为1
//启动服务器线程(可以不用多线程实现,但是为了提高效率,此处用多线程改进)
new Thread(new UserThread(s,count)).start();
System.out.println("连接成功!");
count++; //下一个客户机标记+1
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2.服务器端的多线程实现类UserThread
package day26.tcp.test;
/**
* 服务器多线程实现,用来提高效率
*/
import java.io.*;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class UserThread implements Runnable { //该类实现Runnable接口(多线程前提)
//成员变量定义为私有
private int count;
private Socket s;
//带参构造接收服务器端传来的Socket对象和客户机标记count
public UserThread(Socket s, int count) {
this.s = s;
this.count = count;
}
//必须重写的run()方法
@Override
public void run() {
try {
SimpleDateFormat date = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); //建立连接的时间
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream())); //包装通道里的输出流
//连接成功给出反馈:“连接时间和Welcome+Host+Port”
String _str = s.getInetAddress().getHostName();
String str = "Host:" + _str + "--" + count + "号 port:" + s.getPort() ;
bw.write("Welcome " + str+ " " + date.format(new Date()));
bw.newLine();
bw.flush();
//这里用List代替Vector,因为List可以用Collections工具类实现线程安全,并且比Vector高效。
//将Socket信息添加到集合中
List<Socket> li = Collections.synchronizedList(
new ArrayList<Socket>());
li.add(s);
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream())); //包装输入流
//客户端收到信息后转发送给所有已连接的客户端
String line = null; //先给line初始化
while ((line = br.readLine()) != null) {
if(!li.isEmpty()) { //先判断集合是否为空(增强for使用的前提)
for (Socket _s : li) { //增强for获得集合中所有的Socket对象
bw.write(str + " 的客户说:" + " " + line); //将当前客户机所发的消息转发给所有已连接客户机
bw.newLine();
bw.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.客户端的代码如下:(使用发送接收线程实现)
package day26.tcp.test;
/**
* 编写客户端程序,连接上题服务器,并实现发送线程和接收线程,发送的信息中应包括
* 消息+发送时间,显示消息应包括:Socket+信息+发送时间
* TODO:GUI?(后续完成)
* /
import java.net.*;
import java.io.*;
public class ClientTest {
public static void main(String[] args) throws IOException {
//创建Socket对象,指定ip和端口
Socket s = new Socket("192.168.1.174",12345);
//包装输入输出流,用作参数传递
BufferedReader br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
s.getOutputStream()));
SeThread st = new SeThread(bw); //发送线程类
ReThread rt = new ReThread(br); //接收线程类
//启动发送接收线程
new Thread(new SeThread(bw)).start();
new Thread(new ReThread(br)).start();
}
}
4.客户端的发送线程类SeThread
package day26.tcp.test;
/**
* 客户端发送线程
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SeThread implements Runnable { //实现Runnable接口
//成员变量定义私有
private BufferedWriter bw;
//带参构造,传入输出流对象
public SeThread(BufferedWriter bw) {
this.bw = bw;
}
//重写run()方法
@Override
public void run() {
try {
//包装键盘输入
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
//客户端发送信息
String line = null; //初始化line
while ((line = buf.readLine()) != null) {
if ("886".equals(line)) { //如果客户机输入886
buf.close(); //关闭键盘输入流
bw.close(); //关闭输出流
break;
}
//将客户输入的信息传到输出流:“信息+当前时间”
//客户机发送信息时附带时间
SimpleDateFormat date = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss");
bw.write("“"+line + "”" + " " + date.format(new Date()));
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.客户机接收线程类ReThread
package day26.tcp.test;
/**
* 客户端接收线程
*/
import java.io.BufferedReader;
import java.io.IOException;
public class ReThread implements Runnable { //实现Runnable接口
//成员变量定义私有
private BufferedReader br;
//带参构造,传入输入流对象
public ReThread(BufferedReader br) {
this.br = br;
}
//重写run()方法
@Override
public void run() {
try {
String _line = null; //初始化_line
while ((_line = br.readLine()) != null) {
System.out.println(_line); //打印此客户机收到的信息
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
6.启动服务器程序
7.启动客户端程序,并发送消息
8.由于自己只有一台PC,目前暂时无法完全模仿多客户机同时开启的情况,不过在一台主机上同时开启多个客户端程序,但由于Socket中信息相同,故达不到预期效果。希望自己可以找到解决方案,若解决,就来更新本博文。