网络

简单的client/server程序

  • 先开Server端,再开Client端

TestServer.java

/*  范例名称:简单的client/server程序
 *  源文件名称:TestClient.java/TestServer.java
 *  要  点:
 *      1. Java Socket编程步骤
 *      2. Socket/ServerSocket类用法
 *      3. 通过Socket对象可以获取通信对方Socket的信息
 */
import java.net.*;
import java.io.*;
public class TestServer {
    public static void main(String args[]) {
        try {
            ServerSocket s = new ServerSocket(8888);//Server只开端口接受Socket,Socket需要构造IP地址和端口号
            while (true) {
                Socket s1 = s.accept();
                OutputStream os = s1.getOutputStream();
                DataOutputStream dos = new DataOutputStream(os);
                dos.writeUTF("Hello," + s1.getInetAddress() + "port#" + s1.getPort() + "  bye-bye!");// getInetAddress拿到客户端的IP和端口
                dos.close();
                s1.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("程序运行出错:" + e);
        }
    }
}

TestClient.java

/*  范例名称:简单的client/server程序
 *  源文件名称:TestClient.java/TestServer.java
 *  要  点:
 *      1. Java Socket编程步骤
 *      2. Socket/ServerSocket类用法
 *      3. 通过Socket对象可以获取通信对方Socket的信息
 */
import java.net.*;
import java.io.*;
public class TestClient {
    public static void main(String args[]) {
        try {
            Socket s1 = new Socket("127.0.0.1", 8888);
            InputStream is = s1.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            System.out.println(dis.readUTF());
            dis.close();
            s1.close();
        } catch (ConnectException connExc) {
            connExc.printStackTrace();
            System.err.println("服务器连接失败!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP实现控制台输入

  • 读懂,有不足的地方,第四个里面会讲
  • 实现了控制台手动输入

talkserver.java

import java.io.*;
import java.net.*;
public class talkserver {
    public static void main(String args[]) {
        try {
            ServerSocket server = null;
            try {
                server = new ServerSocket(4700);
            } catch (Exception e) {
                System.out.println("can not listen to:" + e);
            }
            Socket socket = null;
            try {
                socket = server.accept();
            } catch (Exception e) {
                System.out.println("Error:" + e);
            }
            String line;
            BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter os = new PrintWriter(socket.getOutputStream());
            BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Client:" + is.readLine());
            line = sin.readLine();
            while (!line.equals("bye")) {
                os.println(line);
                os.flush();
                System.out.println("Server:" + line);
                System.out.println("Client:" + is.readLine());
                line = sin.readLine();
            }
            is.close();
            os.close();
            socket.close();
            server.close();
        } catch (Exception e) {
            System.out.println("Error" + e);
        }
    }
}

talkclient.java

import java.io.*;
import java.net.*;
public class talkclient {
    public static void main(String args[]) {
        try {
            Socket socket = new Socket("127.0.0.1", 4700);
            BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
            PrintWriter os = new PrintWriter(socket.getOutputStream());
            BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String readline;
            readline = sin.readLine();
            while (!readline.equals("bye")) {
                os.println(readline);
                os.flush();
                System.out.println("Client:" + readline);
                System.out.println("Server:" + is.readLine());
                readline = sin.readLine();
            }
            os.close();
            is.close();
            socket.close();
        } catch (Exception e) {
            System.out.println("Error" + e);
        }
    }
}

TestUDP

TestUDPServer.java

import java.net.*;
public class TestUDPServer {
    public static void main(String args[]) throws Exception {
        byte buf[] = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        DatagramSocket ds = new DatagramSocket(5678);
        while (true) {
            ds.receive(dp);
            System.out.println(new String(buf, 0, dp.getLength()));// 从0开始读到length
        }
    }
}

TestUDPClient.java

//修改前
import java.net.*;
public class TestUDPClient {
    public static void main(String args[]) throws Exception {
        byte[] buf = (new String("Hello")).getBytes();// 将字符串分解为字节数组,放到包里才能发出去
        DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress("127.0.0.1", 5678));
        DatagramSocket ds = new DatagramSocket(9999);// Client本身占据9999,向对方5678端口发消息
        ds.send(dp);
        ds.close();
    }
}

TestUDP 发送长整形数据

  • 可以用于游戏中坐标的传递
  • UDP没有严格意义上的Server和Client区分

TestUDPServer.java

import java.net.*;
import java.io.*;
public class TestUDPServer
{
    public static void main(String args[]) throws Exception
    {
        byte buf[] = new byte[1024];//内存中先分配1K的包袱,但不能直接有数组接
        DatagramPacket dp = new DatagramPacket(buf, buf.length);//包袱外嵌套一个包
        DatagramSocket ds = new DatagramSocket(5678);
        while(true)
        {
            ds.receive(dp);//只要有人发数据,就扔dp包中,阻塞式
            ByteArrayInputStream bais = new ByteArrayInputStream(buf);
            DataInputStream dis = new DataInputStream(bais);//只有dis才能读出long
            System.out.println(dis.readLong());
        }
    }
}

TestUDPClient.java

import java.net.*;
import java.io.*;
//修改后的
public class TestUDPClient {
    public static void main(String args[]) throws Exception {
        long n = 10000L;// 将long类型的数发出去
        ByteArrayOutputStream baos = new ByteArrayOutputStream();// 内存中有一个字节数组,大小自己分配,一个管道装上去了
        DataOutputStream dos = new DataOutputStream(baos);// 只有dos才可以直接往外写数据
        dos.writeLong(n);// 将long类型的数据装到字节数组包里

        byte[] buf = baos.toByteArray();// 转化为字节数组
        System.out.println(buf.length);

        DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress("127.0.0.1", 5678));
        DatagramSocket ds = new DatagramSocket(9999);
        ds.send(dp);
        ds.close();
    }
}

Chat1.0图形界面版

ChatServer.java

import java.net.*;
import java.util.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
public class ChatServer extends Frame
{
    TextArea ta = new TextArea();
    public void launchFrame()
    {
        add(ta, BorderLayout.CENTER);
        setBounds(0,0,200,300); 
        this.addWindowListener(
            new WindowAdapter() 
            {
                public void windowClosing(WindowEvent e)
                {
                    System.exit(0);
                }
            }
            );
        setVisible(true);
    }
    ServerSocket server = null;
    Collection cClient = new ArrayList();
    public ChatServer(int port) throws Exception
    {
        server = new ServerSocket(port);
        launchFrame();
    }
    public void startServer() throws Exception
    {
        while(true)
        {
            Socket s = server.accept();
            cClient.add( new ClientConn(s) );
            ta.append("NEW-CLIENT " + s.getInetAddress() + ":" + s.getPort());
            ta.append("\n" + "CLIENTS-COUNT: " + cClient.size() + "\n\n");
        }
    }
    class ClientConn implements Runnable
    {
        Socket s = null;
        public ClientConn(Socket s)
        {
            this.s = s;
            (new Thread(this)).start();
        }
        public void send(String str) throws IOException
        {
            DataOutputStream dos = new DataOutputStream(s.getOutputStream());
            dos.writeUTF(str);
        }
        public void dispose()
        {
            try {
                if (s != null) s.close();
                cClient.remove(this);
                ta.append("A client out! \n");
                ta.append("CLIENT-COUNT: " + cClient.size() + "\n\n");
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        public void run()
        {
            try {

                DataInputStream dis = new DataInputStream(s.getInputStream());
                String str = dis.readUTF();
                while(str != null && str.length() !=0)
                {
                    System.out.println(str);
                    for(Iterator it = cClient.iterator(); it.hasNext(); )
                    {
                        ClientConn cc = (ClientConn)it.next();
                        if(this != cc)
                        {
                            cc.send(str);
                        }
                    }
                    str = dis.readUTF();
                    //send(str);
                }
                this.dispose();
            } 
            catch (Exception e) 
            {
                System.out.println("client quit");
                this.dispose();
            }
        }
    }
    public static void main(String[] args) throws Exception
    {
        ChatServer cs = new ChatServer(8888);
        cs.startServer();
    }
}

ChatClient.java

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
public class ChatClient extends Frame {
    TextArea ta = new TextArea();
    TextField tf = new TextField();
    public void launchFrame() throws Exception {
        this.add(ta, BorderLayout.CENTER);
        this.add(tf, BorderLayout.SOUTH);
        tf.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                try {
                    String sSend = tf.getText();
                    if (sSend.trim().length() == 0)
                        return;
                    ChatClient.this.send(sSend);
                    tf.setText("");
                    ta.append(sSend + "\n");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        setBounds(300, 300, 300, 400);
        setVisible(true);
        tf.requestFocus();
    }
    Socket s = null;
    public ChatClient() throws Exception {
        s = new Socket("127.0.0.1", 8888);
        launchFrame();
        (new Thread(new ReceiveThread())).start();
    }
    public void send(String str) throws Exception {
        DataOutputStream dos = new DataOutputStream(s.getOutputStream());
        dos.writeUTF(str);
    }
    public void disconnect() throws Exception {
        s.close();
    }
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        ChatClient cc = new ChatClient();
        String str = br.readLine();
        while (str != null && str.length() != 0) {
            cc.send(str);
            str = br.readLine();
        }
        cc.disconnect();
    }
    class ReceiveThread implements Runnable {
        public void run() {
            if (s == null)
                return;
            try {
                DataInputStream dis = new DataInputStream(s.getInputStream());
                String str = dis.readUTF();
                while (str != null && str.length() != 0) {
                    // System.out.println(str);
                    ChatClient.this.ta.append(str + "\n");
                    str = dis.readUTF();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

在线聊天系统雏形

  • Chat1.0的完善
  • 最后的end还讲了Server类中如何再开线程正确去除和监视Client,区分ip和端口,注册名字,但没代码实现
  • 多线程处理多个客户端连接
  • 比较完美的程序
  • 先开服务器,再开客户端
  • 多线程利用的很好
  • 讲解Server端删除关闭的Client端抓取异常
  • Client端的connect(),disconnect(),run()方法是重点
  • Server端的start(),send()抓异常,run()方法抓异常,send()抛异常给run()方法

ChatServer.java

import java.io.*;
import java.net.*;
import java.util.*;
public class ChatServer {//基于TCP的聊天 先启动服务器,后启动客户端
    //启动两遍sever端会报错BindException,一个端口只能监听一个Server
    boolean started = false;
    ServerSocket ss = null;//初始化在try里,这里要定义成null
//    Socket s = null;//初始化在try里,这里要定义成null //因为写了多线程,每个对象都需要新的socket,所以这里可以不写
    ArrayList<Client> clients = new ArrayList<Client>();//面向对象思维,存Client而不存Socket
    public static void main(String[] args) {
        new ChatServer().start();//面向对象的思维方式,大管家生成一个自己类的对象,让他自己执行
    }
    public void start() {//静态main方法中不能生成内部类的对象,不能初始化非静态成员,但可以定义新的变量
                        //new一个内部类,要先new一个外部类,再用外部类对象调用new内部类的方法,或者像这样写一个非静态方法,jingtaimain调用
        try {
            ss = new ServerSocket(8888);//TCP端口号  服务器端从这句话启动
            started = true;//服务器起来了就设置started为true;
        } catch(BindException e) {
            System.out.println("端口使用中");
            System.out.println("请关掉相关程序并重新运行服务器");
            System.exit(0);//这里防止连续启动两次服务器,否则会报错,因为第二个Server会接着执行下面的try,碰到s = ss.accept()
        } catch (IOException e) {
            System.out.println("服务器启动失败");
//            e.printStackTrace();
        }
        try {
            while(started) {
//                boolean bConnected = false; //一定要定义在s的前面,要和started的功能区分开,因为服务器不会随着客户端的断开而关闭//多线程以后就移到线程里了
                 Socket s = ss.accept(); //也是一个阻塞性的方法 接受到了消息,就设置连接为true,两个阻塞式的函数导致只能启动一个客户端
                                 //这里有两种方法,使用异步(较多使用),使用多线程(传统方法),这里使用多线程
                 Client c = new Client(s);
System.out.println("a client connecten");//调试性语句左起打头,到时候要注释方便
                new Thread(c).start();
                clients.add(c);
            }
        }  catch (IOException e) {//这里自己生成的是IOException,但要改成Exception,因为要捕捉上面的EOFException(属于RuntimeException,不属于IOEException)
                              //就算把上面的bConnected在这里设置为FALSE也没用,
                              //后来发现是e.printStackTrace()把捕捉的信息打印出来了,又改回IOException
                              //处理Exception,一定要把和该客户端相应的连接全部关掉
                              //后来又把EOFException另外写出去了,因为只有EOFException对应这客户端关闭,其他是服务器的问题
                              //后来多线程把这块复制放下面了
            e.printStackTrace();
        } finally {//处理中间产生错误而没有处理,在这里处理
            try {
                ss.close();//整个sever关掉
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    class Client implements Runnable{ //名字为Client代表客户端在服务器这里的一个包装
        private Socket s;//每个客户端都有自己的Socket和dis连接,相当于一个插座
        private DataInputStream dis = null;//这里都写上null,因为后面都会加上try,否则会报错
        private DataOutputStream dos = null;//因为Socket定义成了private,所以要用dis和dos都要通过内部类定义的方法调用
        private boolean bConnected = false;
        public Client(Socket s) {
            this.s = s;
            try {
                dis = new DataInputStream(s.getInputStream());
                dos = new DataOutputStream(s.getOutputStream());
                bConnected = true;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
//        public void send(String str) throws IOException { //内部类定义的是private变量,所以用自己定义的函数来调用
////            try {//因为出了SocketException以后这里捉到异常但是remove(c)这里执行不了,只能抛出SocketException或其父类IOException异常让run方法去捉
//                    //后来发现不应该remove(c)之后又把try catch 写回来了
//                dos.writeUTF(str);
////            } catch (IOException e) {
////                e.printStackTrace();
////            }
//        }
        public void send(String str) {
            try {
                dos.writeUTF(str);
            } catch (IOException e) {//意识到是remove(this)后又写了一遍
                clients.remove(this);
                System.out.println("对方退出了,我从list中去除了");
//                e.printStackTrace();
            }
        }
        public void run() {
//            Client c = null;
            try {
                while (bConnected) {
                    String str = dis.readUTF();//阻塞式等待,main线程会阻塞在这里,一直等着有人塞东西给他,当client关掉时可能会报EOF错误,End Of File
System.out.println(str);
                    for(int i = 0; i < clients.size(); i++) { //iterator内部会进行锁定,因为有多个线程访问,但是没必要锁定,只是访问,效率不会太高,所以用i来循环
                        Client c = clients.get(i);
                        c.send(str);
System.out.println("a string send !");
                    }
//                    for(Iterator<Client> it = clients.iterator(); it.hasNext();) {
//                        Client c = it.next();
//                        c.send(str);
//                    }//另外两种实现方式
//                    Iterator<Client> it = clients.iterator();
//                    while(it.hasNext()) {
//                        Client c = it.next();
//                        c.send(str);
//                    }
                } 
//              dis.close();//无论如何都会处理Exception,那里有dis.close(),所以这里就没用了
            } 
//            catch(SocketException e) {//SocketException是IOException子类
////               if(c != null) clients.remove(c);//自己移除掉,其他线程再做循环时就没有这个项了
//                //不应该remove(c),c只是想歪发送的对象,应该去掉调用这个方法的对象this
//                clients.remove(this);
//                System.out.println("a client quit!");
//            } //最后把这里注释掉是因为不能去掉调用run的对象,而应该去掉调用send出错的对象
                //其实第一次写的remove(c)对着呢,只是已经调用了send,在那里还要捕捉相同的错误处理一下
            catch (EOFException e) {
                System.out.println("Client closed");//这里体现出为什么不要直接throws
            } catch (IOException e) {//这里自己生成的是IOException,但要改成Exception,因为要捕捉上面的EOFException(属于RuntimeException,不属于IOEException)
                                  //就算把上面的bConnected在这里设置为FALSE也没用,
                                  //后来发现是e.printStackTrace()把捕捉的信息打印出来了,又改回IOException
                                  //处理Exception,一定要把和该客户端相应的连接全部关掉
                                  //后来又把EOFException另外写出去了,因为只有EOFException对应这客户端关闭,其他是服务器的问题
                e.printStackTrace();
            } finally {//处理中间产生错误而没有处理,在这里处理
                try {
                    if(dis != null) dis.close();//客户端出错了,这里对应客户端的dis也要关掉
                    if(dos != null) dos.close();
                    if(s != null) {
                        s.close();//出错之后马上关掉socket
                        s = null;//内存为空,内存的对象没人应用,垃圾回收器回自动清理
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                }

            }
        } 
    }
}

ChatClient.java

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
public class ChatClient extends Frame{
    Socket s;
    DataOutputStream dos = null;
    DataInputStream dis = null;
    private boolean bConnected = false;
    TextField tfText = new TextField();
    TextArea taContent = new TextArea();
    Thread tRecv = new Thread(new RecvThread());//在这设置是为了能够好关闭他,当对话框关闭时要先彻底关闭另外一个等待读入的线程
    public static void main(String[] args) {
        new ChatClient().launchFrame();
    }
    public void launchFrame() {
        setLocation(100, 100);
        setSize(800, 600);
        setBackground(Color.green);
        add(taContent, BorderLayout.NORTH);
        add(tfText, BorderLayout.SOUTH);
        pack(); //不用pack中间会有一大块背景颜色很难看
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                disconnect();
                System.exit(0);
            }
        });
        tfText.addActionListener(new TFListener());
        setVisible(true);
        connect();
        tRecv.start();//将线程提升至成员变量等级,让其他地方能访问他,可以控制它
    }
    public void connect() {//自己写的方法
        try {//在这都初始化好,后面都可以直接用
            s = new Socket("127.0.0.1", 8888);
            dos = new DataOutputStream(s.getOutputStream());
            dis = new DataInputStream(s.getInputStream());
System.out.println("connected");//连接到服务器上了
            bConnected = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void disconnect() {//有链接创建的资源,就要有断开时清理资源的方法
        try {
            dos.close();// 放到这里是为了不管上面怎么执行,最后都要执行下面的三句话
            dis.close();// 和s的顺序不能换,否则会出错,dis正读着东西呢戒备切断
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
//        try {
//            bConnected = false;//首先另外线程要停止,但还是可能会那个线程已经执行过了while判断再执行的这句话
//            tRecv.join();//把线程合并进来,让它接着执行,这是还没关dis和dos
//            //让线程停止,首要考虑的是join方法,因为这个里面有阻塞式的方法,所以有些不同,最好再起一个线程,等一段时间再不结束,就直接interrupt掉
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            try {
//                dos.close();//放到这里是为了不管上面怎么执行,最后都要执行下面的三句话
//                dis.close();//和s的顺序不能换,否则会出错,dis正读着东西呢戒备切断
//                s.close();
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//        }
    }
    private class TFListener implements ActionListener {//敲回车响应事件
        public void actionPerformed(ActionEvent e) {
            String str = tfText.getText().trim();//去掉两边的空格
//            taContent.setText(str);//输入框传到文本框中
            tfText.setText("");//设置输入框为空
            try {
                dos.writeUTF(str);
                dos.flush();
                //dos.close(); //dos关闭同时会把socket也关闭,所以每次都部去关闭dos
                //网络上的东西关了后要重新连一次才能接着用
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
    private class RecvThread implements Runnable {
        public void run() {
            try {
                while(bConnected) {
                    String str = dis.readUTF();//当窗口关闭时,这里有一个阻塞式的方法,很难处理
//                    System.out.println(str);
                    taContent.setText(taContent.getText() + str + "\n");//不冲掉以前的消息
                }
            } catch (SocketException e) {//最好不要用catch的方法来处理逻辑问题
                System.out.println("退出了, bye!");
            } catch (EOFException e) {
                System.out.println("退出了,bye - bye");
            } catch (IOException e) {
                e.printStackTrace();
            } 
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值