版权声明:DY https://blog.csdn.net/Atishoo_13/article/details/83082702
点对面通信(Socket基于TCP/IP协议)
1.要求
大多情况下,网络通信经常需要多个客户机同一个服务器进行通信,如FTP服务器是同时接收多个客户访问的服务器。本例介绍点对面通信,即一个服务器监听多个客户端的请求的通信。
2.原理
- 创建多客户连接的Sockets通信方式是在服务器端创建客户连接请求的监听线程,一且客户端发起请求,则服务器端创建用于与此客户端通信的线程和Socket,服务器把与此客户的通信交给此线程进行处理。同时,继续在服务器指定端口进行监听,来响应其他客户的服务请求。
- 每一个容户端和眼务器中的线程可以认为是单客户端通信模式下的客户和服务器。一旦Socket和线程建立,服务器主程序会把与某个客户的通信完全交给线程去处理,并利用相应的 Socket完成与客户的通信。
3.语法
- FaceServer类继承服务器端Socket类,因此继承服务器端设置端口的方法。在构造方法中运用super(port)方法继承服务器端的方法,运用标识为真条件进行循环,根据基类的accept()方法创建 Socket对象,循环挂起等待客户端的请求,并根据端口和 Socket对象建立服务线程,监听来自客户端的信息。
- ServerThread类继承线程Thread类,继承 Thread必须实现其run()方法。 ServerThread类的构造方法中初始化端口和 Socket通信对象,并运用通信对象的 getInputStream()方法创建冲读对象,用来获取客户端的数据流。运用通信对象的 getOutputStream()方法创建写往客户端的数据输出流,其中true参数是数据自动刷新从缓存写入到指定位置。调用star()方法启动线程。
- ServerThread类中的run()方法根据标识为真条件进行循环,按每行从获得的数据渣中读取客户端的内容。当读取的内容为exit时设置标识为假,退出循环。
- rw2901类的构造方法初始主机和端口并调用 connectSocket()方法来调用相关主机连接和获得服务器端信息。
- connectSocket()方法判断传入的主机,如果是本机则创建本地Soke连接对象,否则创建远程Sokcket连接对象。根据封装从键盘输入的流创建缓冲读对象,根据 getOutputStream()方法获得服务器端写内容的数据流创建写数据流。运用标识为条件循环读取键盘输入人的每行字符,如果读取的数据为exit則退出循环。缓冲读对象的 readLine()方法获得从服务器端读取的信息。
4.服务器端
代码:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.*;
class FaceServer extends ServerSocket{
private int port; //端口
public FaceServer(int port)throws IOException {
super(port); //继承其类port
this.port=port;
System.out.println("服务器已启动,监听端口号为:"+port);
System.out.println("正在等待客户连接............");
try {
while(true) {
//循环挂起等待客户的请求
Socket socketCon = accept();
new ServerThread(socketCon,port);//建立服务线程
}
}catch(IOException e) {
System.out.println("没有监听到客户端的信息........");
}finally{
close();//关闭通信资源
}
}
}
class ServerThread extends Thread{
//继承线程类实现服务线程
private int port;//端口
private Socket socketCon;
private BufferedReader in;
private PrintWriter out;
public ServerThread(Socket s,int port)throws IOException{
this.port =port;
this.socketCon=s;
in=new BufferedReader(new InputStreamReader(socketCon.getInputStream(),"gb2312"));//读取客户端的数据流
//获取写往客户端的数据输出流,true表示自动刷新
out = new PrintWriter(socketCon.getOutputStream(),true);
out.println("服务器端连接成功.......");//向客户端发送连接信息
out.println("输入exit断开与服务器的连接");
start();//启动线程
}
public String infoUpperCase(String line) {//处理信息
return line.toUpperCase();//将字符串大写
}
public void run() {
try {
boolean done =false;
while(!done) {
String line=in.readLine();//读取客户端每行的内容
if(line==null)
done=true;
else {
//显示客户端发送的内容
System.out.println("客户端传来的内容:"+line);
String message=infoUpperCase(line);//信息处理
//向客户端发送信息
out.println("从服务器端口发送的内容:"+message);
if(line.trim().equals("exit"))//退出判断
done = true;
}
}
System.out.println("客户端终止发送信息.......");
socketCon.close();//关闭通信资源
}catch(Exception e) { //捕获异常
System.out.println("启动服务器端出现错误"+e.getMessage());
}
}
}
public class rw29 {
//操作socket服务器端的类
public static void main(String[] args) {
// TODO Auto-generated method stu
try {
new FaceServer(80); //传入端口实例化对象
}catch(Exception e) { //捕获异常
System.out.println("服务器端进行监听出现异常:"+e.getMessage());
}
}
}
5.客户端
客户端1代码如下:
import java.io.*;
import java.net.*;
class PointClient{
//Socket点客户端
private String host;//IP地址(域名)
private int port; //端口号
public PointClient(String host,int port) {
//带参数构造方法进行初始化
this.host=host;
this.port=port;
connectSocket();//调用连接方法
}
public void connectSocket(){//连接方法
try {
Socket socketConn;//声明Socket连接
//判断IP地址(域名)
if(host.equals("localhost")||host.equals("127.0.0.1")) {
//建立本地连接
socketConn=new Socket(InetAddress.getLocalHost(),port);
}else { //建立远程连接
socketConn=new Socket(InetAddress.getByName(host),port);
}
BufferedReader stdin=new BufferedReader(new InputStreamReader(System.in));//获得从键盘输入的流
PrintWriter out = new PrintWriter(socketConn.getOutputStream(),true);//获得服务器写内容的数据流
BufferedReader in=new BufferedReader(new InputStreamReader(socketConn.getInputStream()));//获得接手服务器发送内容的缓冲流
System.out.println("服务器1信息:"+in.readLine());//从服务器获得信息
System.out.println("服务器1信息:"+in.readLine());
System.out.println("请输入>>");//用户输入
boolean done=false;
while(!done) {
String line=stdin.readLine();//获得从键盘输入的每行字符
out.println(line); //发送到服务端
if(line.equalsIgnoreCase("exit"))//读到exit则结束循环
done=true;
String info =in.readLine();//从服务器读取字符串
System.out.println("服务器1信息:"+info);//显示从服务器发送来的数据
//用户输入
if(!done)
System.out.print("请输入>>");
}
socketConn.close();//关闭资源
}catch(SecurityException e) {//捕获安全性错误时引发的异常
System.out.println("连接服务器出现安全问题!");
}catch(IOException e) {
//捕获IO流异常
System.out.println("连接服务器出现I/O错误!");
}
}
}
public class rw2901 {
//
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
new PointClient("localhost",80);//IP地址为本机,端口为8080
}catch(Exception e){
//捕获异常
System.out.println("测试客户端连接出错:"+e.getMessage());
}
}
}
与客户端1原理相同建立客户端2,在本例中其代码文件名为rw2902,在接下来的结果中将会运行此程序。
6.运行结果
服务器端的输出如下图所示:
客户端1的输出如下图所示:
客户端2的输出如下图所示: