Android通过手机搭建服务器,WIFI建立热点实现C/S聊天室通信功能
应用效果图:
客户端 服务器端
先打开手机服务器,使客户端在同一ip下即可完成wifi热点下通信
一、服务器端
服务器端是用Socket 实现,Socket基础可参考我的上一篇博文《手机服务器微架构设计与实现 之 http server》代码如下:
所需权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
package com.example.lifen.serverdemo; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView; import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections; public class MainActivity extends AppCompatActivity { private MyServer ms;
public static String ipAddress;
private TextView ip; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ms = new MyServer();
ms.startAsync();
ip = (TextView) findViewById(R.id.ip);
ipAddress = getLocalIpAddress();
ip.setText(ipAddress);
} @Override
protected void onDestroy() {
super.onDestroy();
ms.stopAsync();
} public String getLocalIpAddress() {
try {
String ipv4;
ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ni: nilist)
{
ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses());
for (InetAddress address: ialist){
if (!address.isLoopbackAddress() && !address.isLinkLocalAddress())
{
ipv4=address.getHostAddress();
return ipv4;
}
} } } catch (SocketException ex) {
Log.e("localip", ex.toString());
}
return null;
}
}
package com.example.lifen.serverdemo; /**
* Created by LiFen on 2018/1/1.
*/ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Iterator; public class ServerThread implements Runnable {
Socket s = null;
BufferedReader br = null; public ServerThread(Socket s) throws IOException {
this.s = s;
br = new BufferedReader(new InputStreamReader(s.getInputStream(), "utf-8"));
} public void run() {
try {
String content = null;
while ((content = readFromClient()) != null) {
for (Iterator<Socket> it = MyServer.socketList.iterator(); it.hasNext();) {
Socket s = it.next();
try {
OutputStream os = s.getOutputStream();
os.write((content + "\n").getBytes("utf-8"));
} catch (SocketException e) {
e.printStackTrace();
it.remove();
System.out.println(MyServer.socketList);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
} private String readFromClient() {
try {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
MyServer.socketList.remove(s);
}
return null;
}
}
package com.example.lifen.serverdemo; import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList; /**
* Created by LiFen on 2018/1/1.
*/ public class MyServer{
public static ArrayList<Socket> socketList = new ArrayList<>();
private boolean isEnable;
private ServerSocket socket; /**
* 启动Server(异步)
*/
public void startAsync(){
isEnable = true;
new Thread(new Runnable() {
@Override
public void run() {
ServerSocket ss = null;
try {
ss = new ServerSocket(30000);
while (true) {
Socket s = ss.accept();
socketList.add(s);
new Thread(new ServerThread(s)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} /**
* 停止Server(异步)
*/
public void stopAsync() {
if(!isEnable){
return;
}
isEnable = false;
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
socket = null;
}
}
上面服务器线程类不断读取客户端数据,程序使用readFromClient()方法来读取客户数据,如果在读取数据过程中捕获到 Ioexception异常,则表明该 Socket对应的客户端 Socket出现了问题(到底什么问题我们不管,反正不正常),程序就将该 Socket从 socketlist中删除。
当服务器线程读到客户端数据之后,程序遍历 socketlist集合,并将该数据向 socketlist集合中的每个 Socket发送一次该服务器线程将把从 Socket中读到的数据向 socketlist中的每个 Socket转发一次。
二、客户端
package com.example.lifen.multithreadclient; import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView; import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections; public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private EditText input;
private Button send;
private TextView show;
Handler handler;
private ClientThread clientThread;
private TextView ip;
public static String ipAddress; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); input = (EditText) findViewById(R.id.input);
send = (Button) findViewById(R.id.send);
show = (TextView) findViewById(R.id.show);
ip = (TextView) findViewById(R.id.ip);
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 0x123){
show.append("\n" + msg.obj.toString());
}
}
};
clientThread = new ClientThread(handler);
new Thread(clientThread).start(); ipAddress = getLocalIpAddress(); ip.setText("当前IP: "+ipAddress); send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try{
Message msg = new Message();
msg.what = 0x345;
msg.obj = input.getText().toString();
// Log.i(TAG, "onClick: "+msg.obj);
clientThread.revHandler.sendMessage(msg);
input.setText("");
}catch (Exception e) {
Log.e(TAG, "onClick: ", e);
}
}
});
} public String getLocalIpAddress() {
try {
String ipv4;
ArrayList<NetworkInterface> nilist = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ni: nilist)
{
ArrayList<InetAddress> ialist = Collections.list(ni.getInetAddresses());
for (InetAddress address: ialist){
if (!address.isLoopbackAddress() && !address.isLinkLocalAddress())
{
ipv4=address.getHostAddress();
return ipv4;
}
} } } catch (SocketException ex) {
Log.e("localip", ex.toString());
}
return null;
} }
package com.example.lifen.multithreadclient; import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException; /**
* Created by LiFen on 2017/12/31.
*/ public class ClientThread implements Runnable {
private static final String TAG = "ClientThread";
private Socket s;
//定义向UI线程发送消息的Handler 对象
private Handler handler;
//定义接收UI线程的消息 Handler对象
public Handler revHandler;
private BufferedReader br = null;
private OutputStream os = null; public ClientThread(Handler handler){
Log.d(TAG, "ClientThread() called with: handler = [" + handler + "]");
this.handler = handler;
} @Override
public void run() {
Log.d(TAG, "run() called");
try{
String iptemp = MainActivity.ipAddress;
s = new Socket(iptemp,30000);
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
os = s.getOutputStream();
//启动一条子线程来读取服务器响应的数据
new Thread(){
@Override
public void run() {
String content = null;
try {
while((content = br.readLine()) != null){
Message msg = new Message();
msg.what = 0x123;
msg.obj = content;
handler.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
//为当前线程 初始化Looper
Looper.prepare();
//创建revHandler对象
revHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 0x345){
//将用户在文本框内输入网路
try{
os.write((msg.obj.toString() + "\r\n").getBytes("utf-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
// 启动Looper
Looper.loop();
}catch (SocketTimeoutException e1){
Log.i(TAG, "run: 网络连接超时");
} catch (Exception e) {
e.printStackTrace();
}
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.lifen.multithreadclient.MainActivity"> <EditText
android:id="@+id/input"
android:layout_width="244dp"
android:layout_height="47dp"
android:ems="10"
android:inputType="textPersonName"
tools:layout_constraintBottom_creator="1"
android:layout_marginStart="45dp"
app:layout_constraintBottom_toBottomOf="parent"
tools:layout_constraintLeft_creator="1"
android:layout_marginBottom="30dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="45dp"
app:layout_constraintRight_toLeftOf="@+id/send"
android:layout_marginRight="8dp"
app:layout_constraintHorizontal_bias="1.0"
android:layout_marginEnd="8dp" /> <Button
android:id="@+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送"
tools:layout_constraintRight_creator="1"
tools:layout_constraintBottom_creator="1"
app:layout_constraintBottom_toBottomOf="@+id/input"
android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp" /> <TextView
android:id="@+id/ip"
android:layout_width="0dp"
android:layout_height="21dp"
android:text="当前IP:"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:maxLines="1"
tools:layout_constraintRight_creator="1"
tools:layout_constraintBottom_creator="1"
app:layout_constraintBottom_toTopOf="@+id/send"
android:layout_marginStart="22dp"
android:layout_marginEnd="22dp"
app:layout_constraintRight_toRightOf="parent"
tools:layout_constraintLeft_creator="1"
android:layout_marginBottom="5dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintHorizontal_bias="0.09" /> <ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
tools:layout_constraintTop_creator="1"
tools:layout_constraintRight_creator="1"
tools:layout_constraintBottom_creator="1"
android:layout_marginStart="28dp"
app:layout_constraintBottom_toBottomOf="@+id/ip"
android:layout_marginEnd="28dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="16dp"
tools:layout_constraintLeft_creator="1"
android:layout_marginBottom="21dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"> <TextView
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.151"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.105" />
</ScrollView> </android.support.constraint.ConstraintLayout>
当用户单击该程序界面中的“发送”按钮后,程序将会把nput输入框中的内容发送给clientthread的 rehandle对象, clientThread负责将用户输入的内容发送给服务器。
为了避免UI线程被阻塞,该程序将建立网络连接、与网络服务器通信等工作都交给Client thread线程完成,代码47行处启动 ClientThread线程。
由于 Android不允许子线程访问界面组件,因此上面的程序定义了一个 Handler来处理来自线程的消息,如程序中38~45行代码所示。
Client thread子线程负责建立与远程服务器的连接,并负责与远程服务器通信,读到数据之后便通过 Handler对象发送一条消息;当 Client thread子线程收到UI线程发送过来的消息(消息携带了用户输入的内容)后,还负责将用户输入的内容发送给远程服务器。该子线程的代码如上ClientThread代码。
ClientThread线程的功能是不断的获取Soket输出流中的内容,当读到Socket输入流中内容后,便通过Handler对象发送一条消息,消息负责携带读到的数据。该线程还负责读取UI线程发送的消息,接收消息后,该子线程负责将消息中携带的数据发送到远程服务器。
ClientThread子线程中Looper学习
手机服务器项目代码下载:http://download.csdn.net/download/qq_36726507/10183204
手机客户端项目下载:http://download.csdn.net/download/qq_36726507/10183212
Android通过手机搭建服务器,WIFI建立热点实现C/S聊天室通信功能的更多相关文章
- Android 监测手机联网状态 wifi、移动数据流量、无联网状态
手机当完成联网时会发送一个广播,我们只要创建一个广播接收者即可,代码如下: package com.example.NetworkChangeReceiver2; import android.con ...
- Android 基于Socket的聊天室(一)
Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信. Client A ...
- Android简单的聊天室开发(client与server沟通)
请尊重他人的劳动成果.转载请注明出处:Android开发之简单的聊天室(client与server进行通信) 1. 预备知识:Tcp/IP协议与Socket TCP/IP 是Transmission ...
- ubuntu14.10建立热点wifi分享给手机
http://jingyan.baidu.com/article/363872ecd8f35d6e4ba16f97.html ubuntu14.10建立热点wifi分享给手机
- 树莓派搭建钓鱼wifi热点
我们连接的公共wifi其实是非常不安全的网络,骇客可以利用wifi路由设备进行中间人攻击,劫持DNS伪造钓鱼网站.接下来我会做个简单的实验,伪造中国电信的路由ChinaNet并发射出热点wifi等待别 ...
- 手机连上wifi热点后自动弹窗的功能
使用buildroot编译bind DNS服务器 用buildroot来制作文件系统很方便,编译出来的文件系统是直接可用的,不用添加脚本等麻烦的工作,很多的库和app都可以直接添加到文件系统里边,如常 ...
- android 代码设置、打开wifi热点及热点的连接
用过快牙的朋友应该知道它们在两天设备之间传输文件的时候使用的是wifi热点,然后另一台便连接这个热点再进行传输.快牙传输速度惊人应该跟它的这种机制有关系吧.不知道它的搜索机制是怎样的,但我想应该可以通 ...
- android 代码设置、打开wifi热点及热点的连接(转)
用过快牙的朋友应该知道它们在两天设备之间传输文件的时候使用的是wifi热点,然后另一台便连接这个热点再进行传输.快牙传输速度惊人应该跟它的这种机制有关系吧.不知道它的搜索机制是怎样的,但我想应该可 ...
- 利用安卓手机搭建WEB服务器
背景介绍 Android是一种基于Linux的自由及开放源代码的操作系统 所以是用安卓来搭建服务器是完全可行的.接下来将教大家如何利用AndroPHP和Feel FTP(或者其他FTP管理器)来在安卓 ...
随机推荐
- 二十一、springcloud(七)服务网关zuul
1.简介 Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,在微服务架构中,后端 ...
- Maya中输出alembic文件的方法
Maya中输出alembic文件是有现成api调用的,与maya中大部分api一样,这个功能参数的传入是非常类似mel的,本质上讲都是kwargs类型的参数,所以我们传入的参数就需要整理成类似于mel ...
- getattribute
属性访问拦截器 class Itcast(object): def __init__(self,subject1): self.subject1 = subject1 self.subject2 = ...
- iOS上传本地代码到git
1.顾名思义,首先你得注册一个github账户 这个我就不细说了. 2.然后你得创建一个 repository 步骤见下图 3.相当于创建成功 会跳到下图界面 4.一看就很清楚了 create a ...
- CRM 模拟用户
web api 模拟用户 转:https://blog.csdn.net/vic0228/article/details/80649615 var req = new XMLHttpRequest() ...
- Java八大排序算法
Java八大排序算法: package sort; import java.util.ArrayList; import java.util.Arrays; import java.util.List ...
- 简谈OSI七层模型(网络层)
七层模型,亦称OSI(Open System Interconnection)参考模型,是参考模型是国际标准化组织(ISO)制定的一个用于计算机或通信系统间互联的标准体系. 它是一个七层的.抽象的模型 ...
- FragmentTabHost切换Fragment时保存状态,避免切换Fragment走onCreateView和onDestroyView方法;
FragmentTabHost这个控件每次切换Fragment,都会走Fragment的onCreateView和onDestroyView方法,多以每次切换都会创建和销毁Fragment实例,先来看 ...
- leetcode3
public class Solution { public int LengthOfLongestSubstring(string s) { var dic = new Dictionary< ...
- NIO总结
NIO主要有三大核心部分:Channel(通道),Buffer(缓冲区), Selector.传统IO基于字节流和字符流进行操作,而NIO基于Channel和Buffer(缓冲区)进行操作,数据总是从 ...