socket是什么?
1、socket是协议和你应用程序的一个接口,你通过它来实现通信,是用来做通信的一套API
2、socket就是插座的意思,把2个东西通过电线插在插座上,就可以通信了
3、各种通信,现在主要都是用socket的
如何使用socket实现聊天功能?
实现服务端功能,创建一个java项目,用idea和Android studio都可以
推荐使用idea写服务端
这里我们说一说用Android studio创建一个java模块
选择Java Library 需要改名字的自己随意
?app下的就是创建java模块(名字是自己定义的那个)
?
我们来看看代码:
ClientManager.java?(客户端管理类用来管理客户端的消息)
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
// 客户端的管理类
public class ClientManager {
private static Map<String,Socket> clientList = new HashMap<>();
private static ServerThread serverThread = null;
private static class ServerThread implements Runnable {
private int port = 10010;
private boolean isExit = false;
private ServerSocket server;
public ServerThread() {
try {
server = new ServerSocket(port);
System.out.println("启动服务成功" + "port:" + port);
} catch (IOException e) {
System.out.println("启动server失败,错误原因:" + e.getMessage());
}
}
@Override
public void run() {
try {
while (!isExit) {
// 进入等待环节
System.out.println("等待手机的连接... ... ");
final Socket socket = server.accept();
// 获取手机连接的地址及端口号
final String address = socket.getRemoteSocketAddress().toString();
System.out.println("连接成功,连接的手机为:" + address);
new Thread(new Runnable() {
@Override
public void run() {
try {
// 单线程索锁
synchronized (this){
// 放进到Map中保存
clientList.put(address,socket);
}
// 定义输入流
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1){
String text = new String(buffer,0,len);
System.out.println("收到的数据为:" + text);
// 在这里群发消息
sendMsgAll(text);
}
}catch (Exception e){
System.out.println("错误信息为:" + e.getMessage());
}finally {
synchronized (this){
System.out.println("关闭链接:" + address);
clientList.remove(address);
}
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void Stop(){
isExit = true;
if (server != null){
try {
server.close();
System.out.println("已关闭server");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static ServerThread startServer(){
System.out.println("开启服务");
if (serverThread != null){
showDown();
}
serverThread = new ServerThread();
new Thread(serverThread).start();
System.out.println("开启服务成功");
return serverThread;
}
// 关闭所有server socket 和 清空Map
public static void showDown(){
for (Socket socket : clientList.values()) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
serverThread.Stop();
clientList.clear();
}
// 群发的方法
public static boolean sendMsgAll(String msg){
try {
for (Socket socket : clientList.values()) {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(msg.getBytes("utf-8"));
}
return true;
}catch (Exception e){
e.printStackTrace();
}
return false;
}
}
MyClass.java? (开启服务)
public class MyClass {
public static void main(String[]args){
// 开启服务器
ClientManager.startServer();
}
}
这样就可以启动服务端了。(用Android studio的可能会中文乱码和其它问题,想了解的朋友可以更深入的了解,这里就直接用idea写了)
写客户端(Android studio)
1、实现聊天记录显示页面的内容:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="9"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<EditText
android:id="@+id/et"
android:layout_weight="8"
android:layout_width="0dp"
android:hint="输入内容"
android:layout_height="match_parent" />
<Button
android:id="@+id/btn"
android:text="发送"
android:layout_margin="3dp"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
</LinearLayout>
2、接下来是准备工作,首先写一个MyBean,用来存储名字,消息内容,消息时间,以及加载哪种布局:
MyBean.java
public class MyBean {
private String data;
private String time,name;
private int number;
public MyBean() {
}
public MyBean(String data, int number,String time,String name) {
this.data = data;
this.number = number;
this.name = name;
this.time = time;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
3、写两个不同布局的item,第一种内容显示在左侧第二种则在右侧:
第一个 item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#c8fffa"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv"
android:layout_gravity="left"
android:textSize="20sp"
android:text="lalala"
android:layout_margin="5dp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_gravity="left"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_name"
android:text="name_xx"
android:layout_margin="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_time"
android:layout_margin="5dp"
android:text="1993-09-28"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
第二个 item2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#fcfdd9"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv2"
android:layout_gravity="right"
android:textSize="20sp"
android:text="lalala"
android:layout_margin="5dp"
android:textColor="#000000"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_gravity="right"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_name2"
android:text="name_xx"
android:layout_margin="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_time2"
android:layout_margin="5dp"
android:text="1993-09-28"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
4、写一个适配器,用于加载不同的布局(item和item2)
MyAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class MyAdapter extends RecyclerView.Adapter {
private Context context;
private ArrayList<MyBean> data;
private static final int TYPEONE = 1;
private static final int TYPETWO = 2;
public MyAdapter(Context context) {
this.context = context;
}
public void setData(ArrayList<MyBean> data) {
this.data = data;
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
return data.get(position).getNumber();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder holder = null;
switch (viewType){
case TYPEONE:
View view = LayoutInflater.from(context).inflate(R.layout.item,parent,false);
holder = new OneViewHolder(view);
break;
case TYPETWO:
View view1 = LayoutInflater.from(context).inflate(R.layout.item2,parent,false);
holder = new TwoViewHolder(view1);
break;
}
return holder;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int itemViewType = getItemViewType(position);
switch (itemViewType){
case TYPEONE:
OneViewHolder oneViewHolder = (OneViewHolder) holder;
oneViewHolder.tv1.setText(data.get(position).getData());
oneViewHolder.name1.setText(data.get(position).getName());
oneViewHolder.time1.setText(data.get(position).getTime());
break;
case TYPETWO:
TwoViewHolder twoViewHolder = (TwoViewHolder) holder;
twoViewHolder.tv2.setText(data.get(position).getData());
twoViewHolder.name2.setText(data.get(position).getName());
twoViewHolder.time2.setText(data.get(position).getTime());
break;
}
}
@Override
public int getItemCount() {
return data != null && data.size() > 0 ? data.size() : 0;
}
class OneViewHolder extends RecyclerView.ViewHolder{
private TextView tv1;
private TextView name1,time1;
public OneViewHolder(View itemView) {
super(itemView);
tv1 = (TextView) itemView.findViewById(R.id.tv);
name1 = (TextView) itemView.findViewById(R.id.tv_name);
time1 = (TextView) itemView.findViewById(R.id.tv_time);
}
}
class TwoViewHolder extends RecyclerView.ViewHolder{
private TextView tv2;
private TextView name2,time2;
public TwoViewHolder(View itemView) {
super(itemView);
tv2 = (TextView) itemView.findViewById(R.id.tv2);
name2 = (TextView) itemView.findViewById(R.id.tv_name2);
time2 = (TextView) itemView.findViewById(R.id.tv_time2);
}
}
}
5、下面终于进入到了正题~进入到MainActivity中,记得把IP地址改成自己的,看注解
MainActivity.java
package com.example.myapplication;
import android.os.Handler;
import android.os.Message;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private RecyclerView rv;
private EditText et;
private Button btn;
private Socket socket;
private ArrayList<MyBean> list;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rv = (RecyclerView) findViewById(R.id.rv);
et = (EditText) findViewById(R.id.et);
btn = (Button) findViewById(R.id.btn);
list = new ArrayList<>();
adapter = new MyAdapter(this);
final Handler handler = new MyHandler();
new Thread(new Runnable() {
@Override
public void run() {
try {
socket = new Socket("192.168.18.160", 10010); //192.168.18.160是我的IP地址,换成自己电脑ip
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
String data = new String(buffer, 0, len);
// 发到主线程中 收到的数据
Message message = Message.obtain();
message.what = 1;
message.obj = data;
handler.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String data = et.getText().toString();
new Thread(new Runnable() {
@Override
public void run() {
try {
OutputStream outputStream = socket.getOutputStream();
SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); //设置日期格式
outputStream.write((socket.getLocalPort() + "//" + data + "//" + df.format(new Date())).getBytes("utf-8"));
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
//
int localPort = socket.getLocalPort();
String[] split = ((String) msg.obj).split("//");
if (split[0].equals(localPort + "")) {
MyBean bean = new MyBean(split[1], 1, split[2], "我:");
list.add(bean);
} else {
MyBean bean = new MyBean(split[1], 2, split[2], ("来自:" + split[0]));
list.add(bean);
}
// 向适配器set数据
adapter.setData(list);
rv.setAdapter(adapter);
LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this, LinearLayoutManager.VERTICAL, false);
rv.setLayoutManager(manager);
}
}
}
}
6、再加一个网络权限哦
<uses-permission android:name="android.permission.INTERNET"/>
7、来看看效果
?
?好了,想要试试的直接复制粘贴就能运行,(由于我电脑没网,所以电脑和手机链接同一个wifi)
实在不想复制
?点击下载
|