上一篇文章记录的是最基础的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());
                    }
                }
            }
        }
    }
}

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

Java服务端向客户端写文件 java客户端和服务端发送_android

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

Java服务端向客户端写文件 java客户端和服务端发送_Java服务端向客户端写文件_02

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

Java服务端向客户端写文件 java客户端和服务端发送_客户端_03

  • 注:其中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();
        }
    }
}

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

Java服务端向客户端写文件 java客户端和服务端发送_socket_04


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

Java服务端向客户端写文件 java客户端和服务端发送_android_05


  • 最后总结一下我所踩的坑,希望大家引以为戒:
  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() 是单向关闭流。