A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

demo_jean

初级黑马

  • 黑马币:47

  • 帖子:22

  • 精华:0

© demo_jean 初级黑马   /  2017-12-2 19:59  /  1033 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

1. UDP协议基础编程
注意:UDP只有发送端和接收端,不区分客户端和服务端. 可以计算机之间随意的发送数据.
1) 接收端
[Java] 纯文本查看 复制代码
[/align][align=left]      // 创建接收端套接字,接收端口号8888的数据
DatagramSocket ds = new DatagramSocket(8888);
byte[] bytes = new byte[1024];
// 打包数据
DatagramPacket dp = new DatagramPacket(bytes,0,bytes.length);
//接收数据
ds.receive(dp);	//阻塞
// 解析数据
System.out.println("data from : " + dp.getAddress()); // 数据来源的ip地址
System.out.println(new String(dp.getData(), 0, dp.getLength()));	// 打印数据
// 释放资源
ds.close();[/align][align=left]      
2) 发送端
[Java] 纯文本查看 复制代码
// 创建发送端套接字
DatagramSocket ds = new DatagramSocket();
//打包数据
byte[] bytes = "hello,udp~~".getBytes();
// 发送给当前设备 (127.0.0.1), 指定端口号8888
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 8888);
// 发送数据
ds.send(dp);
// 释放资源
ds.close();
2. TCP协议基础编程
注意:TCP协议与UDP不同的是:UDP只有发送端和接收端,不区分客户端和服务端. 可以计算机之间随意的发送数据. TCP,严格区分客户端和服务端,首先启动服务端,必须由客户端先去连接服务器,服务器予以响应才能实现通信
1) 客户端
[Java] 纯文本查看 复制代码
[/align][align=left]      // 创建socket对象,指定端口号,与访问主机ip,向服务器发送请求
Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
// 获取socket的字节输出流
OutputStream os = s.getOutputStream();
// 数据
String str = "hello, TCP, my friend!";
// 写出数据
os.write(str.getBytes());
// 释放资源
s.close();
2) 服务端
// 创建服务端ServerSocket对象,指定端口号,接收该端口发送的数据
                ServerSocket ss = new ServerSocket(8888);
                // 接收数据,获得socket对象,客户端发送请求前处于阻塞状态
                Socket accept = ss.accept();
                // 获得socket的输入流对象
                InputStream is = accept.getInputStream();
                byte[] bytes = new byte[1024];                //接收数据
                int len;        //数据长度
                len = is.read(bytes);        // 读取数据(写入到程序)
                // 打印数据
                System.out.println("data from: " + accept.getInetAddress().getHostAddress());
                System.out.println(new String(bytes, 0, len));
                // 释放资源
                ss.close();
5. TCP模拟用户登录(面向对象)
1) 数据封装类
public class UserData implements Serializable {
        private static final long serialVersionUID = 1L;
        private String name;//姓名
        private String passworld;//密码
        public UserData(String name, String passworld) {        ...}
        public UserData() {        }
        @Override
        public int hashCode() {...}
        @Override
        public boolean equals(Object obj) {....}
}
注意:方法里的内容省略了,注意的是使用了对象输入输出流,所以类必须实现Serializable接口,可序列化.equals(Object obj)必须重写,因为集合中的contains()方法,底层是equal()方法实现的(我们认为用户名和密码匹配上,就是可以登陆的).
2) 用户数据存储
public class UserDB {
        private static ArrayList<UserData> users = new ArrayList<>();
        static {        //静态代码块
                users.add(new UserData("jean", "123456"));
                users.add(new UserData("admin", "password"));
                users.add(new UserData("anni", "qwerqwer"));
                users.add(new UserData("jack", "034235"));
        }
        public static ArrayList<UserData> getUsers() {
                return users;
        }
}
注意:静态代码块是随着系统加载而加载的,虚拟机运行,只执行一次,并且优先于构造代码块和空参构造执行.随着虚拟机的终止而消失.
3) 模拟服务器
                // 创建服务端套接字,指定端口号:8888
                ServerSocket ss = new ServerSocket(8888);
                // 监听
                Socket socket = ss.accept();
                // 获取套接字输出流,并用对象输入流包装
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Object object = ois.readObject();
                UserData use = (UserData) object;
                // 打印流包装,套接字输出流
                PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
                // 判断信息是否存在
                if(UserDB.getUsers().contains(use)) {
                        pw.println("登录成功!!");        //反馈信息
                }else {
                        pw.println("登录失败");                //反馈信息
                }
                // 释放资源
                socket.close();
                //ss.close();
4) 模拟客户端
                // 创建客户端套接字 指定访问主机ip,和端口号
                Socket s = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
                // 键盘录入信息
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                System.out.println("请输入用户名: ");
                String name = br.readLine();
                System.out.println("请输入密码: ");
                String password = br.readLine();
                // 包装数据
                UserData use = new UserData(name, password);
                // 获取客户端套接字输出流,并用对象输出流包装
                ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
                // 发送数据
                oos.writeObject(use);
                // 接收反馈
                BufferedReader br1 = new BufferedReader(new InputStreamReader(s.getInputStream()));
                // 读取反馈
                String line = br1.readLine();
                //打印反馈信息
                System.out.println(line);
                // 释放资源
                s.close();
6. 多线程实现服务器与多客户端之间通信
以上从2-5TCP服务器设计都是单个线程的,仅仅能够处理一个请求,当接收第二个请求时,服务器没有办法处理,但实际万万不能这样使用.正常的服务器,能够处理很多客户端发送的请求,实现这一点就要用到多线程方面的知识. 简单描述一下就是服务器开启处于等待请求的阻塞状态,当有客户端发送请求,创建一个新的线程来处理客户的需求,再来请求,再创建一个线程处理.我们仅仅需要对5设计修改一下.
我们依然使用设计5的客户端,UserDBUserData,
1) 服务器修改
public class Demo5_ThreadServer {
        public static int count = 0; // 请求次数
        public static void main(String[] args) throws IOException, ClassNotFoundException {
                // 创建服务器套接字指定端口号
                ServerSocket ss = new ServerSocket(8888);
                Socket socket = null;
                System.out.println("--------服务器启动,等待客户端发送请求--------");
                while (true) {
                        // 获取客户端套接字
                        socket = ss.accept(); // 阻塞
                        // 创建ThreadServer新线程,处理当前请求
                        new Thread(new ThreadServer(socket)).start();
                }
        }
}
当客户端请求时,我们创建一个新的线程来为客户需求服务,同时保证其他需求进来,一样可以处理.ThreadServer类为Runnable实现类,实现如下:
2) ThreadServer类的实现
class ThreadServer implements Runnable {
        // 创建当前线程socket套接字对象
        private Socket socket = null;
        // 有参构造,传入socket套接字赋值给当前线程socket套接字
        public ThreadServer(Socket socket) {
                this.socket = socket;
        }
        @Override
        public void run() {
                BufferedReader br = null;
                InputStreamReader isr = null;
                InputStream is = null;
                PrintWriter pw = null;
                OutputStream os = null;
                try {
                        is = socket.getInputStream();
                        isr = new InputStreamReader(is);
                        br = new BufferedReader(isr);
                       
                        String name = br.readLine(); // 读取姓名
                        String password = br.readLine(); // 读取密码
                        // br.close();
                        // 封装信息
                        UserData use = new UserData(name, password);
                        // 获取scoket输出流,用打印流进行包装
                        os = socket.getOutputStream();
                        pw = new PrintWriter(os, true);
                        System.out.println("----" + (++Demo5_ThreadServer.count) + "请求来源ip : "
                                        + socket.getInetAddress().getHostAddress() + "----");
                        if (UserDB.getUsers().contains(use)) {
                                System.out.println(name + "请求成功!");
                                pw.println("登录成功!");
                        } else {
                                System.out.println(name + "请求失败!");
                                pw.println("登录失败!");
                        }
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                } finally {
                        try {
                                if (socket != null) {
                                        socket.close();
                                }
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
                }
        }
}
您需要登录后才可以回帖 登录 | 加入黑马