Java网络编程

java网络编程

一.几个相关概念及对应java提供的类

1.ip、域名(会帮我们解析成ip)

InetAddress

package net;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IpDemo {
    public static void main(String[] args) throws Exception {
        InetAddress myAddr=InetAddress.getLocalHost();
        InetAddress baiduAddr=InetAddress.getByName("www.baidu.com");
        System.out.println(myAddr.getHostAddress());
        System.out.println(baiduAddr.getHostAddress());
    }
}

知道有这么个类即可,是代表IP地址的类,方法基本都可以从名字看出来。

2.ip+端口,识别一个进程的类

InetSocketAddress

        InetSocketAddress sock=new InetSocketAddress("localhost",80);
        System.out.println(sock.getAddress()+" "+sock.getPort());

3.URL(具体到某个地址上的具体资源路径 ,跟资源的具体内容无关)

        URL url = new URL("http://www.baidu.com:80/index.html?uname=bjsxt");
        System.out.println("协议:"+url.getProtocol());
        System.out.println("域名:"+url.getHost());
        System.out.println("端口:"+url.getPort());
        System.out.println("资源:"+url.getFile());
        System.out.println("相对路径:"+url.getPath());
        System.out.println("锚点:"+url.getRef()); //锚点
        System.out.println("参数:"+url.getQuery());//?参数 :存在锚点  返回null ,不存在,返回正确

二.UDP聊天实践

package net;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class MyServer extends Thread {
    int port;
    MyServer(int port){
        this.port=port;
    }
    @Override
    public void run() {
        DatagramSocket server= null;
        try {
            server = new DatagramSocket(port);
        } catch (SocketException e) {
            e.printStackTrace();
        }
        while (true)
        {
            byte[] container = new byte[1024];
            DatagramPacket packet=new DatagramPacket(container,container.length);
            try {
                server.receive(packet);
            } catch (IOException e) {
                e.printStackTrace();
            }
            Message m1= JSON.parseObject(packet.getData(),Message.class);
            System.out.println(m1.getSender()+": "+m1.getData());

        }
//        server.close();
    }
}

  1. DatagramSocket(数据报套接字),在某个端口上实现监听(接收客户端的数据请求)
  2. 请求的报文 DatagramPacket,packet.getData()拿到收到的数据(为byte数组,需要进行转换,这里使用fastJson进行转换)
package net;
import com.alibaba.fastjson.JSON;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.util.Scanner;

public class MyClient extends Thread {
    int port;
    String name;
    int myport;
    MyClient(String name,int myport,int port){
        this.name=name;
        this.myport=myport;
        this.port=port;
    }
    @Override
    public void run() {
        DatagramSocket client = null;
        try {
            client = new DatagramSocket(myport);
        } catch (SocketException e) {
            e.printStackTrace();
        }
        Message message=new Message();
        message.setSender(name);
        Scanner sc=new Scanner(System.in);
        while(true)
        {
            String data=sc.nextLine();
            message.setData(data);
            byte[] datas= JSON.toJSONBytes(message);
            DatagramPacket packet = new DatagramPacket(datas,datas.length,new InetSocketAddress("localhost",port));
            try {
                client.send(packet);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println(message.getSender()+": "+message.getData());
            if(data.equals("bye"))
                break;
        }
    }
}
  1. 服务端只需要在某个端口监听,客户端需要两个端口(一个本地端口用来发送,一个是服务器接收你请求的端口)
  2. client = new DatagramSocket(myport); 先通过DataGtamSocket 打开一个本地的端口
  3. 再通过DatagramPacket封装一个数据包对象,并注明接收方的InetSocketAddress(IP与端口)
  4. send把包发出去

三 TCP聊天室实践

1.服务器程序,不断运行接收每个客户端的消息

public class ChatServer {
//    容器来存放每一个channel管道
    private List<MyChannel> all = new ArrayList<MyChannel>();
    public static void main(String[] args) throws IOException {
        ChatServer server=new ChatServer();
        server.start();
    }
    private void start() throws IOException {
        ServerSocket server=new ServerSocket(8888);
        while(true){
            Socket client =server.accept();
            MyChannel channel = new MyChannel(client);
            all.add(channel);//统一管理
            new Thread(channel).start(); //一条道路
        }
    }

关键点解析:

  1. 每个客户端连上后形成了一个输入输出流,相当于产生了一条channel,这条channel的输出流需要输入给其他n-1个客户端(让其他客户端可以收到数据),因此需要一个容器List来存储所有客户端的channel . List all
  2. 服务器通过一个while True保证一直接收客户端的请求,当一个请求进来后(server.accept()),给他一个channel,并且这个channel必须是一个线程,放旁边接着继续运行维护这个客户端的需要。
 public MyChannel(Socket client ) throws IOException {
            dis = new DataInputStream(client.getInputStream());
            dos = new DataOutputStream(client.getOutputStream());
            this.name =dis.readUTF();
//        返回给他自己
            this.send("欢迎您进入聊天室");
            this.sendOthers(this.name+"进入了聊天室",true);

        }
        

channel构造函数关键点解析:

  1. 每个channel都有输入输出流,当新建一个channel的时候,dis就是client的inputStream,dos就是client的outputStream.
 private void send(String msg) throws IOException {
            if(null==msg ||msg.equals("")){
                return ;
            }
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                //e.printStackTrace();
                dis.close();
                dos.close();
                all.remove(this); //移除自身
            }
        }

send函数关键点解析:

  1. dos将msg发出去
 private void sendOthers(String msg,boolean sys) throws IOException {
            //是否为私聊 约定
            if(msg.startsWith("@")&& msg.indexOf(":")>-1 ){ //私聊
                //获取name
                String name =msg.substring(1,msg.indexOf(":"));
                String content = msg.substring(msg.indexOf(":")+1);
                for(MyChannel other:all){
                    if(other.name.equals(name)){
                        other.send(this.name+"对您悄悄地说:"+content);
                    }
                }
            }else{
                //遍历容器
                for(MyChannel other:all){
                    if(other ==this){
                            continue;
                    }
                    if(sys){ //系统信息
                        other.send("系统信息:"+msg);
                    }else{
                        //发送其他客户端
                        other.send(this.name+"对所有人说:"+msg);
                    }
                }
            }
        }

send函数关键点解析:

  1. @开头 并且有对象的时候,为私聊,此时从channel中找出私聊的channel的name,调用该条channel的send函数发送数据。
    2.如果为群聊,则遍历all容器,除自身外所有容器都调用send函数,发给其他每个人。
posted @ 2020-02-27 16:35  彩笔梳子0806  阅读(285)  评论(0)    收藏  举报