Java Socket学习(二)——客户端服务端相互传输数据

来源:互联网 发布:幼儿网络安全教育教案 编辑:程序博客网 时间:2024/05/21 09:29

上一篇文章记录的是最基础的Socket和服务端连接,这篇文章记录的内容是:客户端与服务端相互传输数据的过程,其中客户端为安卓。如有不正确(有争议)的地方希望大家予以指正。

服务端

服务端如题使用Java编写,继续使用上一篇文章的部分代码。

服务端代码

package com.imudges.j2se.network;import com.google.gson.Gson;import java.io.*;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;public class Server {    private Socket socket;    //声明一个ServerSocket对象    private ServerSocket serverSocket;    //输入流    private BufferedReader bufferedReader;    //输出流    private PrintWriter printWriter;    //储存所有连接的socket    private Map<String ,Socket> clientSocket = new HashMap<>();    private Gson gson = new Gson();    /**     * 构造函数     * */    public Server() {}    public static void main(String args[]){        Server server = new Server();        server.getServer();    }    /**     * 获取连接     * */    public void getServer(){        try {            //绑定的端口为6666,此端口要与客户端请求的一致            serverSocket = new ServerSocket(6666);            while(true){                System.err.println("等待客户端连接......");                //从队列中取出Socket或等待连接                socket = serverSocket.accept();                if (socket.isConnected()){                    System.out.println("连接成功!");                }                //开新的线程,为这个socket服务                new HandlerThread(socket);            }        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 实现一个Runnable接口,处理和client的一些事物     * */    private class HandlerThread implements Runnable{        private Socket socket;        public HandlerThread(Socket socket) {            this.socket = socket;            new Thread(this).start();        }        @Override        public void run() {            try {                bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));                //处理客户端发来的数据                String clientMsg = null;                while ((clientMsg = bufferedReader.readLine())!=null){                    System.out.println("客户端发来的消息:" + clientMsg);                    //向客户端回复信息                    printWriter = new PrintWriter(socket.getOutputStream());                    printWriter.println("ok");                    printWriter.flush();                }            } catch (IOException e) {                e.printStackTrace();            } finally {                if(socket != null){                    try {                        socket.close();                    } catch (IOException e) {                        socket = null;                        System.err.println("服务器finally异常,异常信息:" + e.getMessage());                    }                }            }        }    }}

服务端启动之后的控制台如图:

这里写图片描述

有客户端连接后的控制台输出如图:

这里写图片描述

客户端发来消息后的控制台输出如图:

这里写图片描述

  • 注:其中clientID为安卓的IMEI

客户端

客户端为Java,同时将Message封装为了一个Bean。

客户端代码

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout        xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:tools="http://schemas.android.com/tools"        android:layout_width="match_parent"        android:layout_height="match_parent"        tools:context="com.demo.imudges.socketdemo.MainActivity"        android:orientation="vertical">    <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="horizontal"    >        <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="服务器说:"/>        <TextView                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:id="@+id/tv_server_msg"        />    </LinearLayout>    <LinearLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:orientation="horizontal"    >        <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="我给服务器说:"/>        <EditText                android:layout_width="match_parent"                android:layout_height="wrap_content"                android:id="@+id/et_cilent_send_msg"        />    </LinearLayout>    <Button            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="发送信息"            android:gravity="center"            android:id="@+id/btn_send_msg"    /></LinearLayout>

Message.class

package com.demo.imudges.socketdemo;import android.content.Context;import android.widget.Toast;import java.io.*;import java.net.Socket;public class Client {    private Socket socket;    private PrintWriter printWriter;//输出    private BufferedReader bufferedReader;    private static String URL = "183.175.12.168";    private static int port = 6666;    public String msg = "";    private Context context;    public Client(Context context) {        this.context = context;        //设置服务器IP,绑定端口        try {            socket = new Socket(URL,port);            System.out.println("连接完成!");        } catch (IOException e) {            e.printStackTrace();        }    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public void sendMessage(){        try {            //向服务器发送数据            //初始化输出流 用来向服务器传递数据            if(socket.isOutputShutdown()){                System.err.println("OutputStream 被关闭");            }            printWriter = new PrintWriter(socket.getOutputStream(),true);            printWriter.println(msg);            //清空缓冲区的数据流  数据流向:内存->缓冲区->文件(或输出),如果不用.flush(),可能缓冲区内部还有数据残留,.flush()会将缓冲区内部的数据强制输出            printWriter.flush();        } catch (IOException e) {            e.printStackTrace();        }    }    public interface Listener{        void update(String msg);    }    private Listener listener;    public void setListener(Listener l){        this.listener = l;    }    public void getServerMsg(){        //接收服务器数据        //初始化输入流 用来获取服务器下发的数据        try {            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));            String reply = null;            while(!((reply = bufferedReader.readLine()) ==null)){                System.out.println("服务器发送的数据为:" + reply);                listener.update(reply);            }        } catch (IOException e) {            e.printStackTrace();        }    }}

MainActivity.class

package com.demo.imudges.socketdemo;import Bean.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.telephony.TelephonyManager;import android.text.TextUtils;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import com.google.gson.Gson;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private TextView tvServerMsg;    private EditText etCLientSendMsg;    private Button btnSendMsg;    private TelephonyManager telephonyManager;    private String IMEI = "";    private Client client;    private Message message;    private Gson gson = new Gson();    /**     * 初始化控件     * */    private void initViews(){        etCLientSendMsg = (EditText) findViewById(R.id.et_cilent_send_msg);        tvServerMsg = (TextView) findViewById(R.id.tv_server_msg);        btnSendMsg = (Button) findViewById(R.id.btn_send_msg);        btnSendMsg.setOnClickListener(this);        telephonyManager = (TelephonyManager) this.getSystemService(this.TELEPHONY_SERVICE);        //获取设备唯一ID        IMEI = telephonyManager.getDeviceId();    }    /**     * 初始化事件     * */    private void initEvents(){        //判断是否获取到IMEI        if(IMEI != null && !IMEI.equals("")){            new Thread(new Runnable() {                @Override                public void run() {                    client = new Client(MainActivity.this);                    client.setListener(new Client.Listener() {                        @Override                        public void update(final String msg) {                            if(msg!=null){                                //子线程中更新界面                                new Thread(new Runnable() {                                    @Override                                    public void run() {                                        runOnUiThread(new Runnable() {                                            @Override                                            public void run() {                                                //Message messageFromServer = gson.fromJson(msg,Message.class);                                                tvServerMsg.setText(msg);                                            }                                        });                                    }                                }).start();                            }                        }                    });                    client.getServerMsg();                }            }).start();            message = new Message();            message.setClientID(IMEI);        } else {            Toast.makeText(this,"获取IMEI失败",Toast.LENGTH_SHORT).show();        }    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initViews();        initEvents();    }    @Override    public void onClick(View view) {        if(!TextUtils.isEmpty(etCLientSendMsg.getText())){            message.setMsg(etCLientSendMsg.getText().toString());            String msg = gson.toJson(message);            client.setMsg(msg);            new Thread(new Runnable() {                @Override                public void run() {                    client.sendMessage();                }            }).start();        } else {        }    }}

Client.class

package com.demo.imudges.socketdemo;import android.content.Context;import android.widget.Toast;import java.io.*;import java.net.Socket;public class Client {    private Socket socket;    private PrintWriter printWriter;//输出    private BufferedReader bufferedReader;    private static String URL = "183.175.12.168";    private static int port = 6666;    public String msg = "";    private Context context;    public Client(Context context) {        this.context = context;        //设置服务器IP,绑定端口        try {            socket = new Socket(URL,port);            System.out.println("连接完成!");        } catch (IOException e) {            e.printStackTrace();        }    }    public String getMsg() {        return msg;    }    public void setMsg(String msg) {        this.msg = msg;    }    public void sendMessage(){        try {            //向服务器发送数据            //初始化输出流 用来向服务器传递数据            if(socket.isOutputShutdown()){                System.err.println("OutputStream 被关闭");            }            printWriter = new PrintWriter(socket.getOutputStream(),true);            printWriter.println(msg);            //清空缓冲区的数据流  数据流向:内存->缓冲区->文件(或输出),如果不用.flush(),可能缓冲区内部还有数据残留,.flush()会将缓冲区内部的数据强制输出            printWriter.flush();        } catch (IOException e) {            e.printStackTrace();        }    }    public interface Listener{        void update(String msg);    }    private Listener listener;    public void setListener(Listener l){        this.listener = l;    }    public void getServerMsg(){        //接收服务器数据        //初始化输入流 用来获取服务器下发的数据        try {            bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));            String reply = null;            while(!((reply = bufferedReader.readLine()) ==null)){                System.out.println("服务器发送的数据为:" + reply);                listener.update(reply);            }        } catch (IOException e) {            e.printStackTrace();        }    }}

客户端发送消息后的截图:

这里写图片描述


贴上一张在网上找到的图片,个人觉得对于理解服务端和客户端Socket工作原理有所帮助。

这里写图片描述


  • 最后总结一下我所踩的坑,希望大家引以为戒:

  1. 安卓端对于新开的线程,要用.start()方法启动,而不是.run()
  2. 对于Socket接受和发送数据,无论是socket.shutdownInput() socket.shutdownOutput()还是socket.close()他们都会将Socket关闭,所以我使用printWriter.println()发送数据,使用bufferReader.readLint()接受数据,酱紫每次发送一行,每次读取一行,有明确的结束。同时不会使Scoket断开。(下面简单说说其他实现的方法)
    2.1 使用Byte数组发送数据,每次发送的数据的第一位是数据长度,读取方只需要按照发送方给予的长度进行读取就可以,酱紫也不会使Socket断开。
    2.2 发完就断开,断开重新连接。
  3. 理清楚思路再写代码,不要让服务端和客户端同时等待输入或者是同时输出。酱紫会造成死循环?
  4. socket.shutdownOutput() socket.shutdownInput() 是单向关闭流。
阅读全文
0 0