这段时间一直在忙着弄这个东西,做一个服务器能够与外网主机和手机客户端保持一个实时的通信,不断地进行信息的交互。现在很多IT公司都已经做出了成熟的技术解决方案,但是使用过程有点。。繁琐?,还有这些服务器都是要付费的,要花不少钱,作为学生狗自己动手撸。我弄这个花了不少时间,期间试过了百度云、京东云、阿里云(阿里云最坑,公网IP连不上,貌似不止我才这样,其他人买的esc实例也经常连不上),试了这么多还好都是学生优惠,不然心都是在滴血的。最后我用腾讯云基本实现了我想要的功能(还是免费体验的,太实惠了)。下面进入正题
首先购买腾讯云云服务器,按照腾讯云官网的说明选择配置,一步一步下去;连接云服务器的教程官网也有的,用电脑的远程桌面连接比较好,最好选择一个盘作为共享文件,这样你的电脑与云服务器之间传输文件比较方便;安装eclipse和java sdk ,别忘了配置环境变量。
下面是服务端代码:
package test;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static final int PORT = 8800;//监听的端口号
public static void main(String[] args) {
System.out.println("服务器启动...\n");
Server server = new Server();
server.init();
}
public void init() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
while (true) {
//堵塞
Socket client = serverSocket.accept();
new HandlerThread(client);
}
} catch (Exception e) {
System.out.println("服务器异常: " + e.getMessage());
}
}
private class HandlerThread implements Runnable {
private Socket socket;
public HandlerThread(Socket client) {
socket = client;
new Thread(this).start();
}
public void run() {
try {
// 读取客户端数据
DataInputStream input = new DataInputStream(socket.getInputStream());
String clientInputStr = input.readUTF();//这里要注意和客户端输出流的写方法对应,否则会抛 EOFException
// 处理客户端数据
System.out.println("客户端:" + clientInputStr);
// 向客户端回复信息
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
System.out.print("请输入:\t");
// 发送键盘输入的一行
String s = new BufferedReader(new InputStreamReader(System.in)).readLine();
out.writeUTF(s);
out.close();
input.close();
} catch (Exception e) {
System.out.println("服务器异常: " + e.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
socket = null;
System.out.println("服务端异常:" + e.getMessage());
}
}
}
}
}
}
注意:最好不在主线程中去进行连接,一、如果因为网络原因,程序可能回一直卡在那;二、现在很多时候已经不允许在你在主线程中去进行网络有关的操作了。最保险的做法还是新开一个线程去处理这些事情。云主机必须关闭防火墙,查看开放的端口和端口是否被占用。
先来电脑客户端:
package client;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
public class Client {
public static final String IP= "123.207.xx.xx";//服务器地址
public static final int PORT = 8800;//服务器端口号
String DemoAirData=null;
public static void main(String[] args) {
System.out.println("客户端启动...");
System.out.println("当接收到服务器端字符为 \"Exit\" 的时候, 客户端退出\n");
while (true) {
Socket socket = null;
try {
socket = new Socket(IP, PORT);
//读取服务器端数据
DataInputStream input = new DataInputStream(socket.getInputStream());
//向服务器端发送数据
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
System.out.print("请输入: \t");
String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
out.writeUTF(str);
String ret = input.readUTF();
System.out.println("服务器端: " + ret);
// 如接收到 "OK" 则断开连接
if ("OK".equals(ret)) {
System.out.println("客户端将关闭连接");
Thread.sleep(500);
break;
}
out.close();
input.close();
} catch (Exception e) {
System.out.println("客户端异常:" + e.getMessage());
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
socket = null;
System.out.println("客户端异常:" + e.getMessage());
}
}
}
}
}
}
服务器端与客户端的读和写的方法要一致。运行后,服务端和客户端可以反复的进行聊天。
最后是手机客户端:
activit_main.xml代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.administrator.csdn_test2.MainActivity">
<Button
android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/user"
android:text="连接"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_below="@+id/connect"
/>
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/text"
android:hint="输入信息"/>
<Button
android:id="@+id/send"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/edit"
android:text="发送"/>
LinearLayout>
放了四个控件,两个button,一个textview,一个editview,比较简单。
下面是MainActivity
package com.example.administrator.csdn_test2;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
EditText editText;
TextView textView;
Button connect;
public static final int TEXT= 1 ;
public static final int SEND=2;
public static final int CONNECT=3;
String readmsg;
private static final int REQUEST_EXTERNAL_STORAGE = 1 ;
public static final String IP = "123.207.xx.xx";//服务器地址
public static final int PORT = 8800;//服务器端口号
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("客户端启动...");
editText = (EditText) findViewById(R.id.edit);
textView = (TextView) findViewById(R.id.text);
connect=(Button)findViewById(R.id.connect);
Button send=(Button)findViewById(R.id.send);
send.setEnabled(true);
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().
detectDiskWrites().detectNetwork().penaltyLog().build());
connect.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View arg0) {
new myThread().start();
}
});
}
class myThread extends Thread{
@Override
public void run(){
Socket socket=null;
while (true){
try{
Button send=(Button)findViewById(R.id.send);
socket=new Socket(IP,PORT);
final DataOutputStream dataOutputStream=new DataOutputStream(socket.getOutputStream());
DataInputStream dataInputStream=new DataInputStream(socket.getInputStream());
//Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_SHORT).show();
Message message3=new Message();
message3.what=CONNECT;
handler.sendMessage(message3);
send.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (editText.length()==0){
//什么都不做为了防止发空消息
}else {
try{
String string=editText.getText().toString();
dataOutputStream.writeUTF(string);
Message message2=new Message();
message2.what=SEND;
handler.sendMessage(message2);
}catch (IOException e){
e.printStackTrace();
}
}
}
});
readmsg=dataInputStream.readUTF();
Message message1=new Message();
message1.what=TEXT;
handler.sendMessage(message1);
}catch (IOException e){
e.printStackTrace();
}
}
}
}
public Handler handler=new Handler(){//异步消息处理,不能直接在主线程中更新UI,否则会程序会崩溃!
public void handleMessage(Message msg){
switch (msg.what){
case TEXT:
textView.setText(readmsg);
Toast.makeText(MainActivity.this,"收到消息!",Toast.LENGTH_SHORT).show();
break;
case SEND:
editText.setText("");//清空输入框
Toast.makeText(MainActivity.this,"发送成功",Toast.LENGTH_SHORT).show();
break;
case CONNECT:
Toast.makeText(MainActivity.this,"连接成功",Toast.LENGTH_SHORT).show();
connect.setEnabled(true);
default:
break;
}
}
};
}
AndroidManifest.xml中添加权限,android 23以上还需要在java代码中申请权限(我在这里把权限加在application里面又会出错)
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
第一次写博客,时间紧迫就写这么多了,有许多地方也许讲的很浅显,但这都实践得来的经验,对于在下实属不易。希望大家多多指点,共同进步!