UDP Client客户端 http://blog.csdn.net/shankezh/article/details/50731287
UDP Server服务器 http://blog.csdn.net/shankezh/article/details/51452811
TCP Client客户端 http://blog.csdn.net/shankezh/article/details/70763579
TCP Server服务器 http://blog.csdn.net/shankezh/article/details/51555455
----这篇实现以下TCP 服务器,另外由于实在不是想在重复工作了,所以关于tcp多连接服务器代码直接提供思路放在这边了,单一使用请自行删除和移位相关代码即可,原理是一样的,实现方法也有很多种,个人这里只实现其中一种。
首先规划一下页面:
附上对应XML代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="5"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="本机ip:"/>
<TextView
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
android:gravity="center"
android:id="@+id/txt_Server_Ip"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="port"/>
<EditText
android:id="@+id/edit_Server_Port"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="1234"
android:inputType="number" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ID(英文或数字):"/>
<EditText
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="@+id/edit_Server_ID"/>
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="随机生成"
android:id="@+id/btn_tcpServerRandomID"/>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开启"
android:id="@+id/btn_tcpServerConn"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="关闭"
android:id="@+id/btn_tcpServerClose"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清除接收区"
android:id="@+id/btn_tcpCleanServerRecv"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="清除发送区"
android:id="@+id/btn_tcpCleanServerSend"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="2">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:text="接收区:" />
<TextView
android:layout_width="match_parent"
android:layout_weight="5"
android:layout_height="0dp"
android:id="@+id/txt_ServerRcv"
android:background="@android:color/holo_blue_light"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="2">
<TextView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:text="发送区:" />
<TextView
android:id="@+id/txt_ServerSend"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="7"
android:background="@android:color/holo_purple" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="2">
<EditText
android:layout_width="0dp"
android:layout_weight="5"
android:id="@+id/edit_Send"
android:layout_height="match_parent" />
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:layout_gravity="right"
android:id="@+id/btn_tcpServerSend"
android:text="发送"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
界面规划完成,其中关于随机ID这块我没有写代码,有空再补了;
实现TCP 服务器端的关键类在于以下几个类:
1、ServerSocket;
2、Socket;
3、InputSteam;
4、OutputSteam;
5、PrintWriter;
不懂得希望自己搜索查一下,我这就不费话了;
TCP关于服务器建立的流程设计如下:
1、初始化ServerSocket的端口,阻塞监听;
2、监听到连接后,开辟线程,创建对应通信socket流线程,每一个监听到一个对象对应一个新的线程;
3、socket线程内部阻塞监听接收数据,收到后发送给主界面显示;
4、结束循环,对应关闭socket对象;
5、结束监听,关闭ServerSocket对象(同时要注意关闭还在运行的Socket线程及对象);
关于此部分有一些提醒需要说明:
1、serverSocket最好设置超时,因为最终结束时候,还会被阻塞一次,当然也可以利用api强制结束监听异常;
2、Socket最好设置超时,理由同上;
3、关闭ServerSocket时,记得关闭所有的Socket对象,它们还在运行中;
TCP关于服务器发送消息流程:
1、调用相对应的Socket线程中的Send方法即可,利用PrintWriter实现;
具体实现见代码:
package jason.tcpdemo.coms;
import android.content.Intent;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import jason.tcpdemo.funcs.FuncTcpServer;
/**
* Created by Jason Zhu on 2017-04-24.
* Email: [email protected]
*/
public class TcpServer implements Runnable{
private String TAG = "TcpServer";
private int port = 1234;
private boolean isListen = true; //线程监听标志位
public ArrayList<ServerSocketThread> SST = new ArrayList<ServerSocketThread>();
public TcpServer(int port){
this.port = port;
}
//更改监听标志位
public void setIsListen(boolean b){
isListen = b;
}
public void closeSelf(){
isListen = false;
for (ServerSocketThread s : SST){
s.isRun = false;
}
SST.clear();
}
private Socket getSocket(ServerSocket serverSocket){
try {
return serverSocket.accept();
} catch (IOException e) {
e.printStackTrace();
Log.i(TAG, "run: 监听超时");
return null;
}
}
@Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(5000);
while (isListen){
Log.i(TAG, "run: 开始监听...");
Socket socket = getSocket(serverSocket);
if (socket != null){
new ServerSocketThread(socket);
}
}
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public class ServerSocketThread extends Thread{
Socket socket = null;
private PrintWriter pw;
private InputStream is = null;
private OutputStream os = null;
private String ip = null;
private boolean isRun = true;
ServerSocketThread(Socket socket){
this.socket = socket;
ip = socket.getInetAddress().toString();
Log.i(TAG, "ServerSocketThread:检测到新的客户端联入,ip:" + ip);
try {
socket.setSoTimeout(5000);
os = socket.getOutputStream();
is = socket.getInputStream();
pw = new PrintWriter(os,true);
start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String msg){
pw.println(msg);
pw.flush(); //强制送出数据
}
@Override
public void run() {
byte buff[] = new byte[4096];
String rcvMsg;
int rcvLen;
SST.add(this);
while (isRun && !socket.isClosed() && !socket.isInputShutdown()){
try {
if ((rcvLen = is.read(buff)) != -1 ){
rcvMsg = new String(buff,0,rcvLen);
Log.i(TAG, "run:收到消息: " + rcvMsg);
Intent intent =new Intent();
intent.setAction("tcpServerReceiver");
intent.putExtra("tcpServerReceiver",rcvMsg);
FuncTcpServer.context.sendBroadcast(intent);//将消息发送给主界面
if (rcvMsg.equals("QuitServer")){
isRun = false;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
socket.close();
SST.clear();
Log.i(TAG, "run: 断开连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
核心代码完成。
主界面代码(几个类和UDP介绍界面代码时是一样的):
package jason.tcpdemo.funcs;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import jason.tcpdemo.R;
import jason.tcpdemo.coms.TcpServer;
/**
* Created by Jason Zhu on 2017-04-24.
* Email: [email protected]
*/
public class FuncTcpServer extends Activity {
private Button btnStartServer,btnCloseServer, btnCleanServerSend, btnCleanServerRcv,btnServerSend,btnServerRandom;
private TextView txtRcv,txtSend,txtServerIp;
private EditText editServerSend,editServerRandom, editServerPort;
private static TcpServer tcpServer = null;
private MyBtnClicker myBtnClicker = new MyBtnClicker();
private final MyHandler myHandler = new MyHandler(this);
private MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
@SuppressLint("StaticFieldLeak")
public static Context context;
ExecutorService exec = Executors.newCachedThreadPool();
private class MyHandler extends android.os.Handler{
private final WeakReference<FuncTcpServer> mActivity;
MyHandler(FuncTcpServer activity){
mActivity = new WeakReference<FuncTcpServer>(activity);
}
@Override
public void handleMessage(Message msg) {
FuncTcpServer activity = mActivity.get();
if (activity!= null){
switch (msg.what){
case 1:
txtRcv.append(msg.obj.toString());
break;
case 2:
txtSend.append(msg.obj.toString());
break;
}
}
}
}
private class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String mAction = intent.getAction();
switch (mAction){
case "tcpServerReceiver":
String msg = intent.getStringExtra("tcpServerReceiver");
Message message = Message.obtain();
message.what = 1;
message.obj = msg;
myHandler.sendMessage(message);
break;
}
}
}
private void bindReceiver(){
IntentFilter intentFilter = new IntentFilter("tcpServerReceiver");
registerReceiver(myBroadcastReceiver,intentFilter);
}
private class MyBtnClicker implements View.OnClickListener{
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btn_tcpServerConn:
Log.i("A", "onClick: 开始");
btnStartServer.setEnabled(false);
btnCloseServer.setEnabled(true);
btnServerSend.setEnabled(true);
tcpServer = new TcpServer(getHost(editServerPort.getText().toString()));
exec.execute(tcpServer);
break;
case R.id.btn_tcpServerClose:
tcpServer.closeSelf();
btnStartServer.setEnabled(true);
btnCloseServer.setEnabled(false);
btnServerSend.setEnabled(false);
break;
case R.id.btn_tcpCleanServerRecv:
txtRcv.setText("");
break;
case R.id.btn_tcpCleanServerSend:
txtSend.setText("");
break;
case R.id.btn_tcpServerRandomID:
break;
case R.id.btn_tcpServerSend:
Message message = Message.obtain();
message.what = 2;
message.obj = editServerSend.getText().toString();
myHandler.sendMessage(message);
exec.execute(new Runnable() {
@Override
public void run() {
tcpServer.SST.get(0).send(editServerSend.getText().toString());
}
});
break;
}
}
}
private int getHost(String msg){
if (msg.equals("")){
msg = "1234";
}
return Integer.parseInt(msg);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tcp_server);
context = this;
bindID();
bindListener();
bindReceiver();
ini();
}
private void ini(){
btnCloseServer.setEnabled(false);
btnServerSend.setEnabled(false);
txtServerIp.setText(getHostIP());
}
private void bindListener() {
btnStartServer.setOnClickListener(myBtnClicker);
btnCloseServer.setOnClickListener(myBtnClicker);
btnCleanServerRcv.setOnClickListener(myBtnClicker);
btnCleanServerSend.setOnClickListener(myBtnClicker);
btnServerRandom.setOnClickListener(myBtnClicker);
btnServerSend.setOnClickListener(myBtnClicker);
}
private void bindID() {
btnStartServer = (Button) findViewById(R.id.btn_tcpServerConn);
btnCloseServer = (Button) findViewById(R.id.btn_tcpServerClose);
btnCleanServerRcv = (Button) findViewById(R.id.btn_tcpCleanServerRecv);
btnCleanServerSend = (Button) findViewById(R.id.btn_tcpCleanServerSend);
btnServerRandom = (Button) findViewById(R.id.btn_tcpServerRandomID);
btnServerSend = (Button) findViewById(R.id.btn_tcpServerSend);
txtRcv = (TextView) findViewById(R.id.txt_ServerRcv);
txtSend = (TextView) findViewById(R.id.txt_ServerSend);
txtServerIp = (TextView) findViewById(R.id.txt_Server_Ip);
editServerRandom = (EditText) findViewById(R.id.edit_Server_ID);
editServerSend = (EditText) findViewById(R.id.edit_tcpClientSend);
editServerPort = (EditText)findViewById(R.id.edit_Server_Port);
}
/**
* 获取ip地址
* @return
*/
public String getHostIP() {
String hostIp = null;
try {
Enumeration nis = NetworkInterface.getNetworkInterfaces();
InetAddress ia = null;
while (nis.hasMoreElements()) {
NetworkInterface ni = (NetworkInterface) nis.nextElement();
Enumeration<InetAddress> ias = ni.getInetAddresses();
while (ias.hasMoreElements()) {
ia = ias.nextElement();
if (ia instanceof Inet6Address) {
continue;// skip ipv6
}
String ip = ia.getHostAddress();
if (!"127.0.0.1".equals(ip)) {
hostIp = ia.getHostAddress();
break;
}
}
}
} catch (SocketException e) {
Log.i("FuncTcpServer", "SocketException");
e.printStackTrace();
}
return hostIp;
}
}
再次强调,权限:
<uses-permission android:name="android.permission.INTERNET"/>
结束,看效果图:
和写的TCP Client通信效果如下:
开发环境:Android Studio
demo包地址: