网络编程之自己动手写简易聊天室

为了学习web编程,先学习网络编程的基础知识为以后打下基础。网络编程这里使用了TCP协议编程。简易版聊天室实现如下:

首先创造服务端:

class Server{
    public static void main(String[] args) {
        ServerSocket server;
        try {
            server = new ServerSocket(8888);//创建TCP协议端口号为8888的服务端。
            Socket client=server.accept();//接收连接端口号为8888的客户端。
            BufferedInputStream inputStream=new BufferedInputStream(client.getInputStream());//获得两端的输入流以获得客户端发送的消息。同时也可以看出其底层使用的IO流。
            byte[] bytes=new byte[1024];
            inputStream.read(bytes);//读取从客户端发送的消息。并放入在bytes数组中。
            String string=new String(bytes);
            inputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

在写客户端:

class Client{
    public static void main(String[] args) {
        try {
            Socket client=new Socket("localhost",8888);//创建并连接本地的tcp协议的8888端口的客户端。
            String string="李宣霖来访";
            BufferedOutputStream out=new BufferedOutputStream(client.getOutputStream());//获得两端之间的输出流,以便于将消息写入到服务端。
            out.write(string.getBytes());//写入数据
            out.flush();
            out.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

客户端和服务端的输入输出流是相对的,对于服务端来说输出流就是客户端的输入流,客户端的输出流就是服务端的输入流。

这是最基本的客户端和服务端的通信,现对两者升级将发送和接收进行提取封装并为了使发送的同时还可以接受互不干扰,进行并发。我们使用多线程编写。

先写服务端。(使用阻塞式读写流)

public class service {
    public static void main(String[] args) throws IOException {
        ServerSocket ser=new ServerSocket(8888);
        Socket socket=ser.accept();
        DataInputStream input=new DataInputStream(socket.getInputStream());
        DataOutputStream out=new DataOutputStream(socket.getOutputStream());
        
        while(true) {
        String string=input.readUTF();
        out.writeUTF("服务器发送"+string);
        out.flush();
        }
    }
}

客户端编写:

public class Client {

    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket socket=new Socket("LocalHost", 8888);
        Recive recive=new Recive(socket);
        Send send=new Send(socket);
        
        
        Thread thread1=new Thread(recive);
        Thread thread2=new Thread(send);
        thread1.start();
        thread2.start();
    }
}

接收提出封装。

public class Recive implements Runnable {
    private Socket socket;
    
    private DataInputStream input;
    
    private String string=null;
    
    public Recive(Socket socket) {
        this.socket=socket;
    }

    public void run() {
        try {
            input=new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            
            return ;
        }
        try {
            while(true){
            string=input.readUTF();
             if(string !=null)
                {
                    System.out.println("客户端接收"+string);
                }
            }
        } catch (IOException e) {
            try {
                input.close();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            return ;
        }
       
    }

    public String getString() {
        return string;
    }
    
}

发送提出封装。

public class Send implements Runnable{
    private BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
    private DataOutputStream output;
    
    public Send(Socket socket) {
        try {
            output =new DataOutputStream(socket.getOutputStream());
        } catch (IOException e) {
            return;
        }
    }

    public void run() {
        try {
            while(true){
            output.writeUTF(in.readLine());
            output.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

}

这样使得一个客户端可以与服务端并发进行消息的发送和接收。互不干扰。但是还不具备多个客户端进行互相发送接收消息。对此,在现有基础上再次升级。

为了使多个客户端的能够使进行相互间发送端信息我们再次改进,并且当首次建立客户端我们在服务端发送给其他所有的客户端提示:XXX进房间了。当使用@XX:的方式进行输入数据时只允许XX能够接受相关的消息。这样我们需要服务端更够并发的处理相关客户的通信。并且针对从客户端接收的数据进行拆分与分析然后转发即可。为了使服务端可以进行分析转发其他客户端那么我们需要对客户端进行保存和区分,所以我们把客户端的输入输出流和识别名(name 字段)进行封装。但又只有服务端使用该封装类,所以我们将使用内部类进行封装。服务端编写如下:
public class service {
    private List<Mycanel> list=new ArrayList<Mycanel>();//每一个客户端连接后都存储在该链表中。
    public static void main(String[] args) throws IOException {
        new service().start();
    }
    private void start() throws IOException {
        ServerSocket ser=new ServerSocket(8888);
        while(true) {
            Socket socket=ser.accept();
            Mycanel mycanel=new Mycanel(socket);//新客户端添加到链表中去
            list.add(mycanel);
            new Thread(mycanel).start();//为了使服务端可以并发处理相关的客户端我们采用对每个进来的客户端都进行开启新线程
        }
    }
    private  class Mycanel implements Runnable{
        private DataInputStream input;
        private  DataOutputStream out;
        private String name;
        public Mycanel(Socket socket) {
            try {
                
                input=new DataInputStream(socket.getInputStream());
                out=new DataOutputStream(socket.getOutputStream());
                name=input.readUTF();
                this.send("欢迎"+name);
                this.sendOthers(name+"进入聊天室");
                
            } catch (IOException e) {
                return;
            }
            
        }

        
        private String  recive() {
            String msg=null;
            try {
                msg=input.readUTF();
            } catch (IOException e) {
                return null;
                
            }
            
            return msg;
        }
        
        private void send(String msg) {
            try {
                if(msg!=null)
                {
                out.writeUTF(msg);
                out.flush();
                }
            } catch (IOException e) {
                return;
            }
        }
        private void sendOthers(String msg) {
            String name=null;
            String content=null;
            try {
                if(msg!=null) {
                    if(msg.startsWith("@"))
                    {
                        name=msg.substring(1,msg.indexOf(":"));
                        content=msg.substring(msg.indexOf(":")+1,msg.length());
                        
                    }
                    
                        for(Mycanel mycanel:list)
                        {
                            if(mycanel==this)continue;
                            if(mycanel.name.equals(name)||name==null)    
                                  mycanel.send(this.name+"说:"+msg);
                            
                        }
                }
            } catch (Exception e) {
                return;
            }
        }
        public void run() {
            while (true) {
                String msg=recive();
                sendOthers(msg);
            }
            
        }
        
    }
}
客户端编写:

public class Client {

    public static void main(String[] args) throws UnknownHostException, IOException {
        Socket socket=new Socket("LocalHost", 8888);
        Recive recive=new Recive(socket);
        Send send=new Send(socket);
        Thread thread1=new Thread(recive);
        Thread thread2=new Thread(send);
        thread1.start();
        thread2.start();
    }
}
封装好的接收发送:

public class Recive implements Runnable {
    private Socket socket;
    
    private DataInputStream input;
    
    private String string=null;
    
    public Recive(Socket socket) {
        this.socket=socket;
    }

    public void run() {
        try {
            input=new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            
            return ;
        }
        try {
            while(true){
            string=input.readUTF();
             if(string !=null)
                {
                    System.out.println(string);
                }
            }
        } catch (IOException e) {
            try {
                input.close();
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            return ;
        }
    }

    public String getString() {
        return string;
    }
}

public class Send implements Runnable{
    private BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
    private DataOutputStream output;
    private String name=null;
    public Send(Socket socket) {
        try {
            output =new DataOutputStream(socket.getOutputStream());
            System.out.println("登录名:");
            output.writeUTF(in.readLine());
            output.flush();
            
        } catch (IOException e) {
            return;
        }
    }

    public void run() {
        try {
            while(true){
            output.writeUTF(in.readLine());
            output.flush();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }

}

至此客户端可以互相进行交互。这也是网络编程的基础,为web编程学习,了解如何通信打下基础。

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值