上1篇 java socket编程(1)——利用socket实现聊天之消息推送
中我们讲到如何使用socket让服务器和客户端之间传递消息,到达推送消息的目的,接下来我将写出如何让服务器建立客户端与客户端之间的通讯。
其实就是建立1个1对1的聊天通讯。
与上1篇实现消息推送的代码有些不同,在它上面加以修改的。
如果没有提到的方法或类则和上1篇1模1样。
1,UserInfoBean 用户信息表
public class UserInfoBean implements Serializable {
private static final long serialVersionUID = 2L;
private long userId;// 用户id
private String userName;// 用户名
private String likeName;// 昵称
private String userPwd;// 用户密码
private String userIcon;// 用户头像
//省略get、set方法
}
2,MessageBean 聊天信息表
public class MessageBean implements Serializable {
private static final long serialVersionUID = 1L;
private long messageId;// 消息id
private long groupId;// 群id
private boolean isGoup;// 是不是是群消息
private int chatType;// 消息类型;1,文本;2,图片;3,藐视频;4,文件;5,地理位置;6,语音;7,视频通话
private String content;// 文本消息内容
private String errorMsg;// 毛病信息
private int errorCode;// 毛病代码
private int userId;//用户id
private int friendId;//目标好友id
private MessageFileBean chatFile;// 消息附件
//省略get、set方法
}
3,MessageFileBean 消息附件表
public class MessageFileBean implements Serializable {
private static final long serialVersionUID = 3L;
private int fileId;//文件id
private String fileName;//文件名称
private long fileLength;//文件长度
private Byte[] fileByte;//文件内容
private String fileType;//文件类型
private String fileTitle;//文件头名称
//省略get、set方法
}
public class ChatServer {
// socket服务
private static ServerSocket server;
// 使用ArrayList存储所有的Socket
public List<Socket> socketList = new ArrayList<>();
// 模仿保存在内存中的socket
public Map<Integer, Socket> socketMap = new HashMap();
// 模仿保存在数据库中的用户信息
public Map<Integer, UserInfoBean> userMap = new HashMap();
public Gson gson = new Gson();
/**
* 初始化socket服务
*/
public void initServer() {
try {
// 创建1个ServerSocket在端口8080监听客户要求
server = new ServerSocket(SocketUrls.PORT);
createMessage();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 创建消息管理,1直接收消息
*/
private void createMessage() {
try {
System.out.println("等待用户接入 : ");
// 使用accept()阻塞等待客户要求
Socket socket = server.accept();
// 将链接进来的socket保存到集合中
socketList.add(socket);
System.out.println("用户接入 : " + socket.getPort());
// 开启1个子线程来等待另外的socket加入
new Thread(new Runnable() {
public void run() {
// 再次创建1个socket服务等待其他用户接入
createMessage();
}
}).start();
// 用于服务器推送消息给用户
getMessage();
// 从客户端获得信息
BufferedReader bff = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 读取发来服务器信息
String line = null;
// 循环1直接收当前socket发来的消息
while (true) {
Thread.sleep(500);
// System.out.println("内容 : " + bff.readLine());
// 获得客户真个信息
while ((line = bff.readLine()) != null) {
// 解析实体类
MessageBean messageBean = gson.fromJson(line, MessageBean.class);
// 将用户信息添加进入map中,模仿添加进数据库和内存
// 实体类存入数据库,socket存入内存中,都以用户id作为参照
setChatMap(messageBean, socket);
// 将用户发送进来的消息转发给目标好友
getFriend(messageBean);
System.out.println("用户 : " + userMap.get(messageBean.getUserId()).getUserName());
System.out.println("内容 : " + messageBean.getContent());
}
}
// server.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("毛病 : " + e.getMessage());
}
}
/**
* 发送消息
*/
private void getMessage() {
new Thread(new Runnable() {
public void run() {
try {
String buffer;
while (true) {
// 从控制台输入
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
buffer = strin.readLine();
// 由于readLine以换行符为结束点所以,结尾加入换行
buffer += "\n";
// 这里修改成向全部连接到服务器的用户推送消息
for (Socket socket : socketMap.values()) {
OutputStream output = socket.getOutputStream();
output.write(buffer.getBytes("utf⑻"));
// 发送数据
output.flush();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
/**
* 摹拟添加信息进入数据库和内存
*
* @param messageBean
* @param scoket
*/
private void setChatMap(MessageBean messageBean, Socket scoket) {
// 将用户信息存起来
if (userMap != null && userMap.get(messageBean.getUserId()) == null) {
userMap.put(messageBean.getUserId(), getUserInfoBean(messageBean.getUserId()));
}
// 将对应的链接进来的socket存起来
if (socketMap != null && socketMap.get(messageBean.getUserId()) == null) {
socketMap.put(messageBean.getUserId(), scoket);
}
}
/**
* 摹拟数据库的用户信息,这里创建id不同的用户信息
*
* @param userId
* @return
*/
private UserInfoBean getUserInfoBean(int userId) {
UserInfoBean userInfoBean = new UserInfoBean();
userInfoBean.setUserIcon("用户头像");
userInfoBean.setUserId(userId);
userInfoBean.setUserName("admin");
userInfoBean.setUserPwd("123123132a");
return userInfoBean;
}
/**
* 将消息转发给目标好友
*
* @param messageBean
*/
private void getFriend(MessageBean messageBean) {
if (socketMap != null && socketMap.get(messageBean.getFriendId()) != null) {
Socket socket = socketMap.get(messageBean.getFriendId());
String buffer = gson.toJson(messageBean);
// 由于readLine以换行符为结束点所以,结尾加入换行
buffer += "\n";
try {
// 向客户端发送信息
OutputStream output = socket.getOutputStream();
output.write(buffer.getBytes("utf⑻"));
// 发送数据
output.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class LoginActivity extends AppCompatActivity {
private EditText chat_name_text, chat_pwd_text;
private Button chat_login_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
chat_name_text = (EditText) findViewById(R.id.chat_name_text);
chat_pwd_text = (EditText) findViewById(R.id.chat_pwd_text);
chat_login_btn = (Button) findViewById(R.id.chat_login_btn);
chat_login_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int status = getLogin(chat_name_text.getText().toString().trim(), chat_pwd_text.getText().toString().trim());
if (status == -1 || status == 0) {
Toast.makeText(LoginActivity.this, "密码毛病", Toast.LENGTH_LONG).show();
return;
}
getChatServer(getLogin(chat_name_text.getText().toString().trim(), chat_pwd_text.getText().toString().trim()));
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
}
/**
* 返回登陆状态,1为用户,2为另外一个用户,这里摹拟出两个用户相互通讯
*
* @param name
* @param pwd
* @return
*/
private int getLogin(String name, String pwd) {
if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)) {
return 0;//没有输入完全密码
} else if (name.equals("admin") && pwd.equals("1")) {
return 1;//用户1
} else if (name.equals("admin") && pwd.equals("2")) {
return 2;//用户2
} else {
return -1;//密码毛病
}
}
/**
* 实例化1个聊天服务
*
* @param status
*/
private void getChatServer(int status) {
ChatAppliaction.chatServer = new ChatServer(status);
}
}
public class ChatServer {
private Socket socket;
private Handler handler;
private MessageBean messageBean;
private Gson gson = new Gson();
// 由Socket对象得到输出流,并构造PrintWriter对象
PrintWriter printWriter;
InputStream input;
OutputStream output;
DataOutputStream dataOutputStream;
public ChatServer(int status) {
initMessage(status);
initChatServer();
}
/**
* 消息队列,用于传递消息
*
* @param handler
*/
public void setChatHandler(Handler handler) {
this.handler = handler;
}
private void initChatServer() {
//开个线程接收消息
receiveMessage();
}
/**
* 初始化用户信息
*/
private void initMessage(int status) {
messageBean = new MessageBean();
UserInfoBean userInfoBean = new UserInfoBean();
userInfoBean.setUserId(2);
messageBean.setMessageId(1);
messageBean.setChatType(1);
userInfoBean.setUserName("admin");
userInfoBean.setUserPwd("123123123a");
//以下操作模仿当用户点击了某个好友展开的聊天界面,将保存用户id和聊天目标用户id
if (status == 1) {//如果是用户1,那末他就指向用户2聊天
messageBean.setUserId(1);
messageBean.setFriendId(2);
} else if (status == 2) {//如果是用户2,那末他就指向用户1聊天
messageBean.setUserId(2);
messageBean.setFriendId(1);
}
ChatAppliaction.userInfoBean = userInfoBean;
}
/**
* 发送消息
*
* @param contentMsg
*/
public void sendMessage(String contentMsg) {
try {
if (socket == null) {
Message message = handler.obtainMessage();
message.what = 1;
message.obj = "服务器已关闭";
handler.sendMessage(message);
return;
}
byte[] str = contentMsg.getBytes("utf⑻");//将内容转utf⑻
String aaa = new String(str);
messageBean.setContent(aaa);
String messageJson = gson.toJson(messageBean);
/**
* 由于服务器那边的readLine()为阻塞读取
* 如果它读取不到换行符或输出流结束就会1直阻塞在那里
* 所以在json消息最后加上换行符,用于告知服务器,消息已发送终了了
* */
messageJson += "\n";
output.write(messageJson.getBytes("utf⑻"));// 换行打印
output.flush(); // 刷新输出流,使Server马上收到该字符串
} catch (Exception e) {
e.printStackTrace();
Log.e("test", "毛病:" + e.toString());
}
}
/**
* 接收消息,在子线程中
*/
private void receiveMessage() {
new Thread(new Runnable() {
@Override
public void run() {
try {
// 向本机的8080端口发出客户要求
socket = new Socket(SocketUrls.IP, SocketUrls.PORT);
// 由Socket对象得到输入流,并构造相应的BufferedReader对象
printWriter = new PrintWriter(socket.getOutputStream());
input = socket.getInputStream();
output = socket.getOutputStream();
dataOutputStream = new DataOutputStream(socket.getOutputStream());
// 从客户端获得信息
BufferedReader bff = new BufferedReader(new InputStreamReader(input));
// 读取发来服务器信息
String line;
while (true) {
Thread.sleep(500);
// 获得客户真个信息
while ((line = bff.readLine()) != null) {
Log.i("socket", "内容 : " + line);
MessageBean messageBean = gson.fromJson(line, MessageBean.class);
Message message = handler.obtainMessage();
message.obj = messageBean.getContent();
message.what = 1;
handler.sendMessage(message);
}
if (socket == null)
break;
}
output.close();//关闭Socket输出流
input.close();//关闭Socket输入流
socket.close();//关闭Socket
} catch (Exception e) {
e.printStackTrace();
Log.e("test", "毛病:" + e.toString());
}
}
}).start();
}
public Socket getSocekt() {
if (socket == null) return null;
return socket;
}
}
如此1来,代码逻辑已从消息推送的逻辑修改成了单聊的逻辑了。
这个代码可让用户1和用户2相互聊天,并且服务器会记录下他们之间的聊天记录。并且服务器还是具有消息推送的功能。
想要学习的人可以下载demo来研究。