[关闭]
@liayun 2016-07-02T18:42:31.000000Z 字数 11570 阅读 1646

Java 网络编程(二)

java基础


两个案例

例1,需求:上传图片。
解:
定义客户端。
步骤:

  1. 建立服务端点。
  2. 读取客户端已有的图片数据。
  3. 通过Socket输出流将数据发给服务端。
  4. 读取服务端反馈信息。
  5. 关闭资源。
  1. class PicClient {
  2. public static void main(String[] args) throws Exception {
  3. Socket s = new Socket("10.48.16.235", 10007);
  4. FileInputStream fis = new FileInputStream("c:/1.jpg");
  5. OutputStream out = s.getOutputStream();
  6. byte[] buf = new byte[1024];
  7. int len = 0;
  8. while((len=fis.read(buf)) != -1) {
  9. out.write(buf, 0, len);
  10. }
  11. // 告诉服务端数据已写完
  12. s.shutdownOutput();
  13. InputStream in = s.getInputStream();
  14. byte[] bufIn = new byte[1024];
  15. int num = in.read(bufIn);
  16. System.out.println(new String(buf, 0, num));
  17. fis.close();
  18. s.close();
  19. }
  20. }

定义服务端。

  1. class PicServer {
  2. public static void main(String[] args) throws Exception {
  3. ServerSocket ss = new ServerSocket(10007);
  4. Socket s = ss.accept();
  5. InputStream in = s.getInputStream();
  6. FileOutputStream fos = new FileOutputStream("server.jpg");
  7. byte[] buf = new byte[1024];
  8. int len = 0;
  9. while((len=in.read(buf)) != -1) {
  10. fos.write(buf, 0, len);
  11. }
  12. OutputStream out = s.getOutputStream();
  13. out.write("上传成功!".getBytes());
  14. fos.close();
  15. s.close();
  16. ss.close();
  17. }
  18. }

这个服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程。这时B客户端连接,只有等待,因为服务端还没有处理完A客户端的请求,还没有循环回来执行下一次accpet()方法,所以暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端,服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端请求。
那么如何定义线程呢?只要明确了每一个客户端在服务端执行的代码即可。将该代码存入run()方法中。代码如下:

  1. class PicThread implements Runnable {
  2. private Socket s;
  3. PicThread(Socket s) {
  4. this.s = s;
  5. }
  6. public void run() {
  7. int count = 1;
  8. String ip = s.getInetAddress().getHostAddress();
  9. try {
  10. System.out.println(ip+".........connected");
  11. InputStream in = s.getInputStream();
  12. File file = new File(ip+"("+(count)+")"+".jpg"); // 10.48.16.235(1).jpg
  13. while(file.exists()) {
  14. file = new File(ip+"("+(count++)+")"+".jpg");
  15. }
  16. FileOutputStream fos = new FileOutputStream(file);
  17. byte[] buf = new byte[1024];
  18. int len = 0;
  19. while((len=in.read(buf)) != -1) {
  20. fos.write(buf, 0, len);
  21. }
  22. OutputStream out = s.getOutputStream();
  23. out.write("上传成功!".getBytes());
  24. fos.close();
  25. s.close();
  26. } catch (Exception e) {
  27. throw new RuntimeException(ip+"上传失败!");
  28. }
  29. }
  30. }

那么服务端的代码为:

  1. class PicServer {
  2. public static void main(String[] args) throws Exception {
  3. ServerSocket ss = new ServerSocket(10007);
  4. while(true) {
  5. Socket s = ss.accept();
  6. new Thread(new PicThread(s)).start();
  7. }
  8. }
  9. }

以上代码是怎么实现让多个客户端同时并发访问服务端的呢?一张图来理解:

为了让客户端的代码更加严谨,可对要上传的图片路径做一系列判断,如下:

  1. class PicClient {
  2. public static void main(String[] args) throws Exception {
  3. if(args.length != 1) {
  4. System.out.println("请选择一个jpg格式的图片");
  5. return;
  6. }
  7. File file = new File(args[0]);
  8. if(!(file.exists() && file.isFile())) {
  9. System.out.println("该文件有问题,要么不存在,要么不是文件");
  10. return;
  11. }
  12. if(!file.getName().endsWith(".jpg")) {
  13. System.out.println("图片格式错误,请重新选择");
  14. return;
  15. }
  16. if(file.length() > 1024*1024*5) {
  17. System.out.println("文件过大,没安好心");
  18. return;
  19. }
  20. Socket s = new Socket("10.48.16.235", 10007);
  21. FileInputStream fis = new FileInputStream(file);
  22. OutputStream out = s.getOutputStream();
  23. byte[] buf = new byte[1024];
  24. int len = 0;
  25. while((len=fis.read(buf)) != -1) {
  26. out.write(buf, 0, len);
  27. }
  28. // 告诉服务端数据已写完
  29. s.shutdownOutput();
  30. InputStream in = s.getInputStream();
  31. byte[] bufIn = new byte[1024];
  32. int num = in.read(bufIn);
  33. System.out.println(new String(buf, 0, num));
  34. fis.close();
  35. s.close();
  36. }
  37. }

例2,客户端通过键盘录入用户名,服务端对这个用户名进行校验。如果该用户存在,在服务端显示XXX,已登录,并在客户端显示XXX,欢迎光临。如果该用户不存在,在服务端显示XXX,尝试登录,并在客户端显示XXX,该用户不存在。最多就登录3次。
服务端用户数据为:

  1. zhangsan
  2. lisi
  3. wangwu
  4. sunba
  5. zhouqi

解:

演示客户端和服务端

URL类

类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。
此类必须掌握以下方法:

例,以下代码

  1. class URLDemo {
  2. public static void main(String[] args) throws MalformedURLException {
  3. URL url = new URL("http://10.48.16.235:8888/myWeb/demo.html?name=haha&age=30");
  4. System.out.println("getProtocol():"+url.getProtocol());
  5. System.out.println("getHost():"+url.getHost());
  6. System.out.println("getPort():"+url.getPort());
  7. System.out.println("getPath():"+url.getPath());
  8. System.out.println("getFile():"+url.getFile());
  9. System.out.println("getQuery():"+url.getQuery());
  10. // 不写端口时,给一个默认的端口号80
  11. /*
  12. int port = url.getPort();
  13. if(port == -1)
  14. port = 80;
  15. */
  16. }
  17. }

输出:

getProtocol():http
getHost():10.48.16.235
getPort():8888
getPath():/myWeb/demo.html
getFile():/myWeb/demo.html?name=haha&age=30
getQuery():name=haha&age=30

URLConnection抽象类

此抽象类代表应用程序和URL之间的通信链接。此类的实例可用于读取和写入此URL引用的资源。它封装了Socket
示例代码如下:

  1. class URLConnectionDemo {
  2. public static void main(String[] args) throws Exception {
  3. URL url = new URL("http://192.168.1.116:8888/myWeb/demo.html");
  4. URLConnection conn = url.openConnection();
  5. System.out.println(conn); // 输出:sun.net.www.protocol.http.HttpURLConnection:http://192.168.1.116:8888/myWeb/demo.html
  6. InputStream in = conn.getInputStream();
  7. byte[] buf = new byte[1024];
  8. int len = in.read(buf);
  9. System.out.println(new String(buf, 0, len));
  10. }
  11. }

所以自定义图形界面浏览器代码优化之后为:

  1. import java.awt.*;
  2. import java.awt.event.*;
  3. import java.io.*;
  4. import java.net.*;
  5. class MyIEByGUI2 {
  6. private Frame f;
  7. private TextField tf;
  8. private Button but;
  9. private TextArea ta;
  10. private Dialog d;
  11. private Label lab;
  12. private Button okBut;
  13. MyIEByGUI2() {
  14. init();
  15. }
  16. public void init() {
  17. f = new Frame("my window");
  18. f.setBounds(300, 100, 600, 500);
  19. f.setLayout(new FlowLayout());
  20. tf = new TextField(60);
  21. but = new Button("转到");
  22. ta = new TextArea(25, 68);
  23. /*
  24. 对话框也是一个窗体,最好不要加到Frame里面去
  25. */
  26. d = new Dialog(f, "提示信息-self", true); // true:对话框不处理掉,后面的窗体是无法操作的!!
  27. d.setBounds(400, 200, 240, 150);
  28. d.setLayout(new FlowLayout());
  29. lab = new Label();
  30. okBut = new Button("确定");
  31. d.add(lab);
  32. d.add(okBut);
  33. f.add(tf);
  34. f.add(but);
  35. f.add(ta);
  36. myEvent();
  37. f.setVisible(true);
  38. }
  39. private void myEvent() {
  40. // 点击对话框中的确定按钮,对话框也不显示出来
  41. okBut.addActionListener(new ActionListener() {
  42. public void actionPerformed(ActionEvent e) {
  43. d.setVisible(false);
  44. }
  45. });
  46. // 关闭对话框,对话框不显示
  47. d.addWindowListener(new WindowAdapter() {
  48. public void windowClosing(WindowEvent e) {
  49. d.setVisible(false);
  50. }
  51. });
  52. tf.addKeyListener(new KeyAdapter() {
  53. public void keyPressed(KeyEvent e) {
  54. try {
  55. if(e.getKeyCode() == KeyEvent.VK_ENTER)
  56. showDir();
  57. } catch (Exception ex) {
  58. }
  59. }
  60. });
  61. but.addActionListener(new ActionListener() {
  62. public void actionPerformed(ActionEvent e) {
  63. try {
  64. showDir();
  65. } catch (Exception ex) {
  66. }
  67. }
  68. });
  69. f.addWindowListener(new WindowAdapter() {
  70. public void windowClosing(WindowEvent e) {
  71. System.exit(0);
  72. }
  73. });
  74. }
  75. private void showDir() throws Exception {
  76. ta.setText("");
  77. String urlPath = tf.getText(); // http://192.168.1.116:8888/myWeb/demo.html
  78. URL url = new URL(urlPath);
  79. URLConnection conn = url.openConnection();
  80. InputStream in = conn.getInputStream();
  81. byte[] buf = new byte[1024];
  82. int len = in.read(buf);
  83. ta.setText(new String(buf, 0, len));
  84. }
  85. public static void main(String[] args) {
  86. new MyIEByGUI2();
  87. }
  88. }

图示其中的原理:

域名解析

域名解析用一张图说明:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注