Android中网络编程以及与服务器上Web项目的基础交互

该博文中内容通过老师上课的内容对于Android下的网络通信编程等内容进行总结;

1、Android网络编程初识

2、Android实现HTML源码查看

3、Android实现网络图片查看

4、Android实现与服务器上JavaWeb项目交互

1、Android网络编程初识

  • Android手机终端作为客户端发送Http请求到终端服务器上,并且当发送请求到服务器,如果请求成功,响应码:200;服务器会通过客户端返回请求并且处理后的信息数据,一般情况下,在正式项目中多用JSON,不会使用没有规定格式的文本信息;
  • 注意事项:Android的网络操作在Android4之后,就不能够将Android的网络操作写在主线程中除了会直接造成应用崩溃之外,还因为一旦造成主线程阻塞,应用会停止刷新界面,停止响应用户任何操作,用户体验非常差,所以耗时操作不要写在主线程中;
  • 将于网路连接部分写在子线程中,再通过handler实现了通信的相关操作。


2、Html源文件查看器(掌握)

  • 发送GET请求
public class MainActivity extends Activity {

    private Handler handler;
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) this.findViewById(R.id.tv);
        handler = new Handler() {
            //重写消息队列方法
            @Override
            public void handleMessage(Message msg) {
                tv.setText((String) msg.obj);
            }

        };
    }

    public void click(View view) {
        final String path = "http://192.168.1.109:8080/baidu.html";
        Thread thread = new Thread() {
            @Override
            public void run() {
                try {
                    //使用网址创建URL
                    URL url = new URL(path);
                    //获取连接对象做设置
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    //设置请求方式
                    conn.setRequestMethod("GET");
                    //设置超时时间
                    conn.setConnectTimeout(8000);
                    //设置读取超时的时间
                    conn.setReadTimeout(8000);
                    //连接
                    conn.connect();
                    //连接成功
                    if(conn.getResponseCode() == 200) {
                        System.out.println("Connected======");
                        InputStream is = conn.getInputStream();
                        String text = StreamTool.getTextFromStream(is);

                        Message msg = handler.obtainMessage();
                        msg.obj = text;
                        handler.sendMessage(msg);

                    } else if(conn.getResponseCode() == 404) {

                    }
                } catch (MalformedURLException e) {
                    System.out.println("MalFormed Exception=");
                } catch (IOException e) {
                    System.out.println("IOException");
                }
            }
        };
        thread.start();
    }
}
  • 在上面的代码中获取服务器返回的流,从流中把html源码读取出来
        InputStream is = conn.getInputStream();
        String text = StreamTool.getTextFromStream(is);
        Message msg = handler.obtainMessage();
        msg.obj = text;
        handler.sendMessage(msg);
        ////////////////////////
        handler = new Handler() {
            //重写消息队列方法
            @Override
            public void handleMessage(Message msg) {
                tv.setText((String) msg.obj);
            }
        };
  • 乱码的处理
  • 乱码的出现是因为服务器和客户端码之间的编码表的不一致所导致的,所以自己写一个专门处理乱码的工具类方便调用
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;

    public class StreamTool {
        public static String getTextFromStream(InputStream is) {
            byte[] buffer = new byte[1024];
            int len = 0;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                while((len = is.read(buffer)) != -1) {
                    bos.write(buffer, 0, len);
                }
            //利用字节数组输出流转换成字节数组,然后用字节数组构造成为一个字符串
                String text = new String(bos.toByteArray());
                return text;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";
        }
    }


3、Android实现网络图片查看

  • 确定图片的网址
  • 实现功能
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
/**
 * 几个知识点必须要掌握的:
 * 1、4.3版本以上的编程规范,否则谷歌会强制不予执行,应为为了用户体验不会让主线程出现长时间的阻塞,否则会造成用户体验变差;
 * 2、在新创建出来的线程中不可以直接使用UI否则会出现
 * 11-25 02:27:08.884: E/AndroidRuntime(7093): java.lang.RuntimeException: Can‘t create handler inside thread that has not called Looper.prepare()
 * 错误,是因为在子线程中会丢失对于UI的相关的参数设置
 * 3、消息队列相关的概念必须要很好的掌握,在主线程开始创建的是会提供对应的详细队列的机制,并且会用Logger消息轮询器进行检索访问
 * @author DELL
 *
 */
public class MainActivity extends Activity {

    private ImageView iv;
    private Bitmap bm;

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        //只要消息队列中有消息,此方法就会在主线程中执行
        public void handleMessage(android.os.Message msg) {
            //在这里进行消息队列的刷新,从而达到刷新UI的目的
            switch (msg.what) {
            case 1:
                iv = (ImageView) findViewById(R.id.iv);
                iv.setImageBitmap((Bitmap)msg.obj);
                break;
            case 2:
                Toast.makeText(MainActivity.this, "加载视图失败", Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //版本在4.0以上必须要使用的两个操作
        //1、事件添加的时候需要新建线程
        //2、设置builder
        if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }
    }
    /**
     * 注意一点只有主线程可以刷新UI
     * @param view
     */
    public void clickload(View view) {
        final String path = "http://192.168.2.197:8080/img/e.jpg";
        final File file = new File(getCacheDir(),getFileNameFromPath(path));
        if(file.exists()) {
            System.out.println("============缓存获取==============");
            bm = BitmapFactory.decodeFile(file.getAbsolutePath());
            Message msg = new Message();
            msg.obj = bm;
            msg.what = 1;
            handler.sendMessage(msg);
        } else {
            System.out.println("============网络获取==============");
            Thread thread = new Thread(){
                @Override
                public void run() {
                    try { //发送http请求的操作步骤
                        //1、创建URL对象
                        URL url = new URL(path);
                        //2、获取连接对象
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        //3、设置一些属性
                        //设置请求方式,注意大写
                        conn.setRequestMethod("GET");
                        //设置请求超时
                        conn.setConnectTimeout(8000);
                        //设置读取超时
                        conn.setReadTimeout(8000);
                        //4、发送请求建立连接
                        conn.connect();  /*可能阻塞的位置*/
                        //5、判断请求是否成功
                        if(conn.getResponseCode() == 200) {  /*可能阻塞的位置*/
                            //获取服务器返回的刘,流中就是客户端的请求数据
                            System.out.println("====true");
                            InputStream is = conn.getInputStream();
                            //现在需要我们自己读取流中的数据,读取1K,就可以把1K写到本地文件缓存起来
                            byte[] buffer = new byte[1024];
                            int len ;
                            FileOutputStream fos = new FileOutputStream(file);
                            while((len = is.read(buffer)) != -1) {
                                fos.write(buffer, 0, len);
                            }
                            fos.close();
                            //缓存时候已经将缓存中的数据读取完了,所以流中取不到任何数据
//                          bm = BitmapFactory.decodeStream(is);
                            bm = BitmapFactory.decodeFile(file.getAbsolutePath());
                            //当子线程需要刷新UI的时候,只需要发送一条消息到消息队列
                            //发送消息的方法,在另一端接受消息队列
                            Message msg = new Message();
                            //消息对象本身是可以携带数据的
                            msg.obj = bm;
                            //使用what标注消息是什么类型
                            msg.what = 1;//我自身定义1表示成功

                            handler.sendMessage(msg);
                        } else {
                            //创建消息对象
                            //如果消息池中有消息,取出消息池中第一条消息
                            //返回,如果没有,就new一个消息返回
                            Message msg = handler.obtainMessage();
                            msg.what = 2;//我自身定义2表示加载失败
                            handler.sendMessage(msg);
                            System.out.println("====false");
                        }
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                        Log.e("MalformedURLException", "URL创建过程出错");
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        Log.e("IOException", "HttpURLConnection出错");
                    }
                }
            };

            thread.start();
        }
    }
    //路径获取图片文件名称
    public String getFileNameFromPath(String path) {
        int index = path.lastIndexOf("/");
        return path.substring(index+1);
    }
}


4、Android实现与服务器上JavaWeb项目交互

  • 普通方式:使用GET方式提交表单
  • 首先实现一个工具类:
import java.io.ByteArrayOutputStream;
import java.io.InputStream;

public class Tools {
    public static String getTextFromStream(InputStream is) {
        byte[] buffer = new byte[1024];
        int len;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            while((len = is.read(buffer))!=-1) {
                bos.write(buffer,0,len);
            }
            String text = new String(bos.toByteArray(),"utf-8");
            return text;
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}
  • 服务端
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        System.out.println("======GET======");
        String username = request.getParameter("username");

        String password = request.getParameter("password");

        username = new String(username.getBytes("ISO-8859-1"),"utf-8");
        password = new String(password.getBytes("ISO-8859-1"),"utf-8");
        username = username.trim();
        password = password.trim();

        System.out.println(username + "    " + password);
        if("admin".equalsIgnoreCase(username)) {
            response.getWriter().print("登陆成功");
        } else {
            response.getWriter().print("登陆失败");
        }
    }
  • Android 客户端
public class MainActivity extends Activity {
    private EditText et_username;
    private EditText et_password;
    private String username;
    private String password;
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
                break;
            default:
                Toast.makeText(MainActivity.this, "网络异常", Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username = (EditText) this.findViewById(R.id.username);
        et_password = (EditText) this.findViewById(R.id.password);
    }

    public void login(View view) {
        username = et_username.getText().toString().trim();
        password = et_password.getText().toString().trim();
        Thread thread = new Thread() {
            String path = "http://192.168.1.105:8080/AndroidWebServerDemo/Login?username=+"+URLDecoder.decode(username)+"+&password=+"+URLDecoder.decode(password);
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setConnectTimeout(8000);
                    conn.setReadTimeout(8000);
                    conn.setRequestMethod("GET");
                    conn.connect();

                    if(conn.getResponseCode() == 200) {
                        InputStream is = conn.getInputStream();
                        String text = Tools.getTextFromStream(is);
                        System.out.println(text);
                        Message msg = new Message();
                        msg.obj = text;
                        msg.what = 1;
                        handler.sendMessage(msg);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
    }
}

  • 普通方式:使用POST方式提交表单
  • 服务端
@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        System.out.println("======POST======");
        String username = request.getParameter("username");

        String password = request.getParameter("password");

//      username = new String(username.getBytes("ISO-8859-1"),"utf-8");
//      password = new String(password.getBytes("ISO-8859-1"),"utf-8");

        username = username.trim();
        password = password.trim();

        System.out.println(username + "  " + password);
        if("admin".equalsIgnoreCase(username)) {
            response.getWriter().print("登陆成功");
        } else {
            response.getWriter().print("登陆失败");
        }
    }
  • Android 客户端
  • readTool类与上文中GET方式的Tool相同
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.android.getmethod.R;
import com.android.postmethod.read.tools.Tools;

public class MainActivity extends Activity {

    private EditText et_username;
    private EditText et_password;
    private String username;
    private String password;

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 1:
                Toast.makeText(MainActivity.this, (String) msg.obj, Toast.LENGTH_SHORT).show();
                break;

            default:
                Toast.makeText(MainActivity.this, "网络异常", Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username = (EditText) this.findViewById(R.id.username);
        et_password = (EditText) this.findViewById(R.id.password);
    }

    public void login(View view) {
        username = et_username.getText().toString().trim();
        password = et_password.getText().toString().trim();
        Thread thread = new Thread() {
            String path = "http://192.168.1.105:8080/AndroidWebServerDemo1/Login";
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setConnectTimeout(8000);
                    conn.setReadTimeout(8000);
                    conn.setRequestMethod("POST");

                    //添加POST请求头中自动添加的属性
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    //方便服务器端的程序员,通过程序的这个数据知道数据的大小
                    //实现动态的大小长度
                    String content_text = "username="+ URLEncoder.encode(username) + "&password=" + password;
                    //流中数据的长度
                    conn.setRequestProperty("Content-Length", content_text.length()+"");

                    //打开连接对象的输出流
                    conn.setDoOutput(true);
                    //把数据写入到输入流中
                    OutputStream os = conn.getOutputStream();
                    //将数据写入到输出流中
                    os.write(content_text.getBytes());

                    conn.connect();
                    if(conn.getResponseCode() == 200) {
                        InputStream is = conn.getInputStream();
                        String text = Tools.getTextFromStream(is);
                        System.out.println(text);
                        Message msg = new Message();
                        msg.obj = text;
                        msg.what = 1;
                        handler.sendMessage(msg);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
    }
}

  • 使用HTTPClient的方式实现与服务器的数据交互
  • GET方式(HTTPClient)
 public class MainActivity extends Activity {

    private EditText et_username;
    private EditText et_password;

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String text = (String) msg.obj;
            Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username = (EditText) this.findViewById(R.id.username);
        et_password = (EditText) this.findViewById(R.id.password);
    }

    public void login(View view) {
        //获取用户输入的账号密码
        Thread thread = new Thread() {
            String username = et_username.getText().toString();
            String password = et_password.getText().toString();

            final String path = "http://192.168.1.105:8080/AndroidWebServerDemo1/Login?" +
                    "username="+username+"&password="+password;
            public void run() {
                System.out.println("=============================================="+path);
                //创建HttpClient对象
                HttpClient client = new DefaultHttpClient();
                //创建get请求对象
                HttpGet get = new HttpGet(path);
                try {
                    //使用client发送get请求
                    HttpResponse response = client.execute(get);
                    //获取状态行
                    StatusLine line = response.getStatusLine();
                    //获取状态码
                    if(line.getStatusCode() == 200) {
                        System.out.println("==============================================200");
                        //获取实体对象
                        InputStream is = response.getEntity().getContent();
                        String text = Tools.getTextFromStream(is);

                        Message msg = handler.obtainMessage();
                        msg.obj = text;
                        handler.sendMessage(msg);
                    } else {
                        System.out.println("=============================================="+line.getStatusCode());
                    }
                } catch (Exception e) {
                    System.out.println("==============================================错误");
                    e.printStackTrace();
                }
            };
        };

        thread.start();
    }

}
  • POST 方式(HTTPClient)
public class MainActivity extends Activity {

    private EditText et_username;
    private EditText et_password;

    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String text = (String) msg.obj;
            Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_username = (EditText) this.findViewById(R.id.username);
        et_password = (EditText) this.findViewById(R.id.password);
    }

    public void login(View view) {
        //获取用户输入的账号密码
        Thread thread = new Thread() {
            String username = et_username.getText().toString();
            String password = et_password.getText().toString();
            final String path = "http://192.168.1.105:8080/AndroidWebServerDemo1/Login";
            public void run() {
                System.out.println("====="+path);
                //创建HttpClient对象
                HttpClient client = new DefaultHttpClient();
                //创建post请求对象
                HttpPost post = new HttpPost(path);
                //将数据封装到post中
                List<NameValuePair> parameters = new ArrayList<NameValuePair>();
                //arg0:表单的名字
                //arg1:表单中的值
                BasicNameValuePair bnvp1 = new BasicNameValuePair("username", username);
                BasicNameValuePair bnvp2 = new BasicNameValuePair("password", password);

                parameters.add(bnvp1);
                parameters.add(bnvp2);
                //创建实体对象
                UrlEncodedFormEntity entity = null;
                try {
                    //将集合对象封装到实体中
                    entity = new UrlEncodedFormEntity(parameters,"utf-8");
                    post.setEntity(entity);
                    //数据响应
                    HttpResponse response = client.execute(post);

                    if(response.getStatusLine().getStatusCode() == 200) {
                        InputStream is = response.getEntity().getContent();
                        String text = Tools.getTextFromStream(is);
                        Message msg = handler.obtainMessage();
                        msg.obj = text;
                        msg.what = 1;
                        handler.sendMessage(msg);
                    }

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            };
        };

        thread.start();
    }

}
时间: 2024-10-01 05:00:11

Android中网络编程以及与服务器上Web项目的基础交互的相关文章

项目总结50:Linux服务器上web项目Java项目性能调优

项目总结50:Linux服务器上web项目Java项目性能调优 最近上线的电商项目,发现非常卡,用户体验非常差,折腾了好久之后,也逐渐找到原因,并针对原因解决方案,先整理总结. 项目基本情况: 1-使用阿里ECS.OSS等一系列相关服务: 2-用户总量1W+,日活量500+ 3-电商项目,有APP.小程序.管理平台三个模块,其中接口150+ 4-项目使用SSM框架: 5-项目tomcat服务,数据库Mysql,Redis放在一个同一个服务器上: 问题表现: 1-接口反应非常慢,导致APP和小程序

android数据库编程:连接服务器上的MySQL数据库详细示例

1 public class DatabaseDemo extends Activity { 2 private TextView textView; 3 4 @Override 5 protected void onCreate(Bundle savedInstanceState) { 6 // TODO Auto-generated method stub 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.acti

Android 的网络编程

android的网络编程分为2种:基于socket的,和基于http协议的. 基于socket的用法 服务器端: 先启动一个服务器端的socket     ServerSocket svr = new ServerSocket(8989); 开始侦听请求 Socket s = svr.accept(); 取得输入和输出 DataInputStream dis = new DataInputStream(s.getInputStream()); DataOutputStream dos = new

网络编程 --- URLConnection --- 读取服务器的数据 --- java

使用URLConnection类获取服务器的数据 抽象类URLConnection表示一个指向指定URL资源的活动连接,它是java协议处理器机制的一部分. URL对象的openConnection()方法就是调用了URLStreamHandler的openConnection()方法. 如有疑问请参考:JAVA网络编程[第三版], 如下图: 怎样获取服务器输出的数据呢?代码如下: import java.io.IOException; import java.io.InputStream; i

Linux网络编程:客户端/服务器的简单实现

一. Socket的基本知识 1. socket功能 Socket层次 Socket实质上提供了进程通信的端点,进程通信之前,双方必须首先各自创建一个端点,否则是没有办法建立联系并相互通信的. 每一个Socket都一个半相关描述: {协议, 本地地址, 本地端口} 完整的Socket的描述: {协议, 本地地址, 本地端口, 远程地址, 远程端口} 2. Socket工作流程 面向连接(TCP)的Socket工作流程 UDP的socket工作流程 l 服务器端 首先,服务器应用程序用系统调用so

Android中实现java与PHP服务器(基于新浪云免费云平台)http通信详解

Android中实现java与PHP服务器(基于新浪云免费云平台)http通信详解 (本文转自: http://blog.csdn.net/yinhaide/article/details/44756989) 前言:现在很多APP都需要云的功能,也就是通过网络与服务器交换数据.有的采用tcp/ip协议,但是你必须拥有一个固定ip的服务器,可以购买阿里云服务器之类的,就是贵了点.如果只是个人的小应用的的话可以采用新浪云平台这种免费的服务器,采用的协议是http协议,具体实现方式如下: 方式一.在线

android中网络操作使用总结(http)

Android是作为智能手机的操作系统,我们开发的应用,大多数也都需要连接网络,通过网络发送数据.获取数据,因此作为一个应用开发者必须熟悉怎么进行网络访问与连接.通常android中进行网络连接一般是使用scoket或者http,http是最多的情况,这里,我来总结下,怎么进行http网络访问操作. android是采用java语言进行开发的,android的包中包含java的URLConnection和apache 的httpclient,因此我们可以使用这两个工具进行网络连接和操作.同时,为

Android中多线程编程(四)AsyncTask类的详细解释(附源码)

Android中多线程编程中AsyncTask类的详细解释 1.Android单线程模型 2.耗时操作放在非主线程中执行 Android主线程和子线程之间的通信封装类:AsyncTask类 1.子线程中更新UI 2.封装.简化异步操作. 3.AsyncTask机制:底层是通过线程池来工作的,当一个线程没有执行完毕,后边的线程是无法执行的.必须等前边的线程执行完毕后,后边的线程才能执行. AsyncTask类使用注意事项: 1.在UI线程中创建AsyncTask的实例 2.必须在UI线程中调用As

java 中网络编程

网络编程 IP地址: 主机在网络中的逻辑地址 Port: 标定主机中的进程 一个进程绑定一个端口 协议: 网络双方约定的通信标准 应用 表示 应用 HTTP FTP TELNET 会话 传输 传输 网络 网络 寻址和路由 数据链路 网络接口 TCP/IP 物理 传输 TCP 传输控制协议 连接 安全可靠 UDP 用户数据报协议 无连接 不可靠 Socket 套接字 java 中网络编程