[1]实验目的:理解Socket通信原理,掌握使用Socket和ServerSocket类进行TCP Socket通信的程序设计方法。
[2]实验内容:
1、使用ServerSocket类和Socket类实现按如下协议通信的服务器端和客户端程序。
服务器程序的处理规则如下:
客户端程序的处理规则如下:
服务器端:
package experiment6.exp6_1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;
public class Exp6_1_Server {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(6001);
Socket server = socket.accept();
String password;
int count = 0;
PrintWriter out = new PrintWriter(server.getOutputStream(), true);//返回TCP连接提供的字节输出流,将其格式化
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));//字节输入流转换为字符输入流,
//BufferedReader 提供以缓冲方式读取一行字符串的方法
out.println("Verifying Server!");
while (true) {
password = in.readLine(); //读取客户端数据
System.out.println("Client:" + password);
if (Objects.equals(password, "123456")) {
out.println("Registration Successful!");
System.out.println("Server:Registration Successful!");
System.exit(0);
} else {
count++;
if (count == 3) {
out.println("Illegal User!");
System.out.println("Server: Illegal User!");
break;
} else {
out.println(" PassWord Wrong!");
System.out.println("Server: PassWord Wrong!");
}
}
}
out.close();
in.close();
server.close();
socket.close();
}
}
客户端
package experiment6.exp6_1;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Exp6_1_Client {
public static void main(String[] args) throws IOException {
Socket ser=new Socket("10.200.229.170",6001);
Scanner scanner =new Scanner(System.in);
String str,sc;
PrintWriter out=new PrintWriter(ser.getOutputStream(),true);
BufferedReader in=new BufferedReader(new InputStreamReader(ser.getInputStream()));
while(true)
{
str=in.readLine();
System.out.println("Server:"+str);
if(str.equals("Illegal User!")||str.equals("Registration Successful!"))
{
break;
}
System.out.print("Client:");
sc= scanner.next();
out.println(sc);
}
out.close();
in.close();
ser.close();
}
}
登录失败情况:
Server:
Client:
登录成功情况:
Server:
Client:
2、实现聊天室功能。
1)完成服务器端和一个客户端之间的聊天功能。
2)扩展部分,采用多线程技术实现一个服务器端和多个客户端之间的聊天功能。
服务器端
package experiment6.exp6_2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
public class server implements Runnable {// 服务端
static List socketList= new ArrayList<>();
// 读取 In
static Socket socket = null;
static ServerSocket serverSocket = null;
public server() {// 构造方法
try {
serverSocket = new ServerSocket(9999);
} catch (IOException e) {
e.printStackTrace();
}
}
int offline =0;
public static void main(String[] args) {
System.out.println("Server");
server t = new server();
System.out.println("Port 9999 remains to be connected......");
int count = 0;
while (true) {
try {
socket = serverSocket.accept();
count++;
System.out.println(count + " client(s) connected successfully!");
socketList.add(socket);
} catch (IOException e) {
e.printStackTrace();
}
Print p = new Print(socket);
Thread read = new Thread(t);
Thread print = new Thread(p);
read.start();
print.start();
}
}
@Override
public void run() {
// 重写run方法
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
while (true) {
//接收消息
String jieshou = in.readLine();
if(jieshou == null){
System.out.println("One client got offline");
offline++;
if(offline ==socketList.size()){
System.out.println("All clients offline. Server offline now!");
serverSocket.close();
socket.close();
System.exit(0);
}
break;}
else
System.out.println(jieshou);
//向每一个客户发送接受的消息
for (int i = 0; i < socketList.size(); i++) {
Socket socket = socketList.get(i);
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.println(jieshou);
out.flush();
}
}
} catch (Exception e) {
}
}
}
class Print implements Runnable {
static List socketList= new ArrayList<>();
Scanner input = new Scanner(System.in);
public Print(Socket s) {// 构造方法
try {
socketList.add(s);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String msg = bufferedReader.readLine();
if(Objects.equals(msg, "/quit")) System.exit(0);
for (int i = 0; i < socketList.size(); i++) {
//对每一个客户端发送消息
Socket socket=socketList.get(i);
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.println("Server: "+msg);
out.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端
package experiment6.exp6_2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Objects;
import java.util.Scanner;
public class client implements Runnable {// 客户端
static Socket socket = null;
Scanner input = new Scanner(System.in);
String name;
client(String name){this.name=name;}
public void main1() {
System.out.println("Client "+name);
try {
socket = new Socket("localhost", 9999);
System.out.println("Connected!");
} catch (Exception e) {
e.printStackTrace();
}
client t = new client(name);
Read r = new Read(socket);
Thread print = new Thread(t);
Thread read = new Thread(r);
print.start();
read.start();
}
@Override
public void run() {
try {
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
while (true) {
//向客户端发送消息
String msg = bufferedReader.readLine();
if(Objects.equals(msg, "/quit")){
socket.close();
System.exit(0);
}
out.println(name+": "+msg);
out.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Read implements Runnable {
static Socket socket = null;
public Read(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
while (true) {
//接受服务器的消息
System.out.println(in.readLine());
}
} catch (Exception e) {
System.out.println("Server offline! Connection Dismissed!");
System.exit(0);
}
}
}
//client1
package experiment6.exp6_2;
import java.util.Scanner;
public class client1 {
public static void main(String[] args){
System.out.println("请输入昵称:");
Scanner scanner = new Scanner(System.in);
client client_1 = new client(scanner.next());
client_1.main1();
}
}
//client2
package experiment6.exp6_2;
public class client2 {
public static void main(String[] args){
client client_2 = new client("fang");
client_2.main1();
}
}
Server:
Client1:
Client2:
[3]实验分析
第一题比较简单,只有单个客户端和单个服务器端的简单通信,使用PrintWriter返回TCP连接提供的字节输出流,将其格式化,用BufferReader将字节输入流转化为字符输入流,同时提供以缓冲方式读取一行字符串的方法。
对于第二题,为了实现一个服务器端和多个客户端之间的通信,将读和写的功能分别置于两个线程当中,服务器端用一个socket列表来存放与所有客户端建立连接的socket,每发生一个连接,启动新的读写线程来进行通信。客户端和服务器端用BufferReader来进行整行字符串的读取。
在服务器端接收到某个客户端的消息后,还会将其进行转发,发送至每一个客户端,是的所有客户端之间能看见互相发送的消息。同时,为客户端和服务器端设立退出关键字“/quit”,输入“/quit”之后,客户端会关闭socket,结束程序,客户端下线;服务器端则会关闭所有socket,使得所有客户端也被迫退出通信。如果所有客户端均退出,服务器端自动关闭。
客户端也具备自己决定昵称的功能。