首先说明原理:并非客户端与客户端之间直接通信 而是通过建立服务器端 将每个客户端的信息都发送到同一个服务器端项目上,然后利用服务器端将收到的信息再广播到所有的socket客户端。如果只是实验或者只需要局域网内的通信,只需要建立java项目启动来代替服务器端,如果需要实现公网多个客户端交流,则只需要将java项目放在云服务器,将socket端口更改成公网地址即可,下面是具体各个项目的代码,
1.下面服务器端代码MyClass.java
自己修改服务器地址和端口号即可
public class MyClass{
static List cons = new LinkedList();
private static Socket socket = null;
public static class ServerThread extends Thread{
private Socket s;
public ServerThread(Socket socket){
this.s = socket;
cons.add(s);
}
@Override
public void run(){
System.out.print("新用户加入\n");
System.out.print("当前在线数量:"+cons.size()+"\n");
try{
while(true){
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String mess;// = br.readLine();
//保存信息
if((mess=br.readLine())!=null) {
if(mess.equals("用户退出")){
s.close();
}
System.out.print("客户端:" + mess + "\n");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//为了证明是服务器返回的数据,我对mess修改在发送到客户端
//这里修改广播到所有客户端
for (Socket so : cons){
BufferedWriter buffw = new BufferedWriter(new OutputStreamWriter(so.getOutputStream()));
String str = "服务器>>"+mess+"\n";
buffw.write(str);
buffw.flush();
}
//
//单客户端通信
/*
String str = "服务器>>"+mess+"\n";
bw.write(str);
bw.flush();
*/
}
}
}catch (IOException e){
System.out.print("用户退出!\n");
cons.remove(s);
e.printStackTrace();
this.interrupt();
//e.printStackTrace();
}catch (NullPointerException e) {
System.out.print("NullPointerException");
}finally {
try {
s.close();
}catch (IOException e){
System.out.print("IOException-2");
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
System.out.println("服务器启动成功");
ServerSocket ss = new ServerSocket(30001);
while(true){
Socket s = ss.accept();
new ServerThread(s).start();
}
}
}
2下面是安卓主程序,还有用到子项布局适配器类 和消息包装类
MainActivity.java
public class MainActivity extends AppCompatActivity {
private List msgList=new ArrayList<>();
private EditText inputText;
private Button send;
private RecyclerView msgRecyclerView;
private MsgAdapter adapter;
private Socket socket=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化消息数据
inputText=(EditText)findViewById(R.id.input_text);
send=(Button)findViewById(R.id.send);
msgRecyclerView=(RecyclerView)findViewById(R.id.msg_recycler_view);
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
//LinearLayoutLayout即线性布局,创建对象后把它设置到RecyclerView当中
msgRecyclerView.setLayoutManager(layoutManager);
adapter=new MsgAdapter(msgList);
//创建MsgAdapter的实例并将数据传入到MsgAdapter的构造函数中
msgRecyclerView.setAdapter(adapter);
send.setOnClickListener(new View.OnClickListener(){
//发送按钮点击事件
@Override
public void onClick(View v){
String content=inputText.getText().toString();
new Thread (new sendthead(content)).start();
//获取EditText中的内容
if(!"".equals(content)){
//内容不为空则创建一个新的Msg对象,并把它添加到msgList列表中
Msg msg=new Msg(content,Msg.TYPE_SENT);
msgList.add(msg);
adapter.notifyItemInserted(msgList.size()-1);
//调用适配器的notifyItemInserted()用于通知列表有新的数据插入,这样新增的一条消息才能在RecyclerView中显示
msgRecyclerView.scrollToPosition(msgList.size()-1);
//调用scrollToPosition()方法将显示的数据定位到最后一行,以保证可以看到最后发出的一条消息
inputText.setText("");
//调用EditText的setText()方法将输入的内容清空
}
}
});
new Thread(new Runnable() {
@Override
public void run() {
String msg_get;
try{
socket=new Socket();
socket.connect(new InetSocketAddress("172.20.10.6",30001),10000);
while(true){
BufferedReader bw = new BufferedReader(new InputStreamReader(socket.getInputStream()));
msg_get = bw.readLine();
Message msg = new Message();
Bundle bundle = new Bundle();
bundle.putString("newcontent", msg_get);
msg.setData(bundle);
handler.sendMessage(msg);}
}catch(IOException E){
E.printStackTrace();
}
}
}).start();
} android.os.Handler handler = new android.os.Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String newcontent = bundle.getString("newcontent");
Msg msg1=new Msg(newcontent,Msg.TYPE_RECEIVED);
if(msg1!=msgList.get(msgList.size()-1))
msgList.add(msg1);
adapter.notifyItemInserted(msgList.size()-1);
//调用适配器的notifyItemInserted()用于通知列表有新的数据插入,这样新增的一条消息才能在RecyclerView中显示
msgRecyclerView.scrollToPosition(msgList.size()-1);
}
};
class sendthead extends Thread{
private String x;
public sendthead(String m){
this.x=m;
}
@Override
public void run() {
try{
BufferedWriter br=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br.write(x);
br.newLine();
br.flush();
}
catch(IOException E){E.printStackTrace();}
}
}
}
消息包装类
public class Msg {
public static final int TYPE_RECEIVED=0;
public static final int TYPE_SENT=1;
private String content;
private int Type;
public Msg(String content,int type){
this.content=content;
this.Type=type;
}public int getType(){
return Type;
}
public String getContent(){
return content;
}
}
子项布局适配器类
public class MsgAdapter extends RecyclerView.Adapter {
private List mMsgList;
static class ViewHolder extends RecyclerView.ViewHolder{
LinearLayout leftLayout;
LinearLayout rightLayout;
TextView leftMsg;
TextView rightMsg;
public ViewHolder(View view){
super(view);
leftLayout=(LinearLayout)view.findViewById(R.id.left_layout);
rightLayout=(LinearLayout)view.findViewById(R.id.right_layout);
leftMsg=(TextView)view.findViewById(R.id.left_msg);
rightMsg=(TextView)view.findViewById(R.id.right_msg);
}
}
public MsgAdapter(List msgList){
mMsgList=msgList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
//onCreateViewHolder()用于创建ViewHolder实例
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);
return new ViewHolder(view);
//把加载出来的布局传到构造函数中,再返回
}
@Override
public void onBindViewHolder(ViewHolder Holder,int position){
//onBindViewHolder()用于对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行
Msg msg=mMsgList.get(position);
if(msg.getType()==Msg.TYPE_RECEIVED){
//增加对消息类的判断,如果这条消息是收到的,显示左边布局,是发出的,显示右边布局
Holder.leftLayout.setVisibility(View.VISIBLE);
Holder.rightLayout.setVisibility(View.GONE);
Holder.leftMsg.setText(msg.getContent());
}else if(msg.getType()==Msg.TYPE_SENT) {
Holder.rightLayout.setVisibility(View.VISIBLE);
Holder.leftLayout.setVisibility(View.GONE);
Holder.rightMsg.setText(msg.getContent());
}
}
public int getItemCount(){
return mMsgList.size();}}
下面是子项布局:msg_item.xml
适配器 子项布局是为了将从服务器和自己发送的信息按照一定的格式显示在UI界面上。建议大家不明白机制的先学习一下。
同时需要在主布局下建立RecycleView
主布局activity_main.xml
另外java客户端代码以及全部代码内容由于内容过多,百云不可上传。可以直接私信或者评论加邮箱。本人新手深知自学android过程不易,开源万岁!!!