Android系统回顾(八):网络通信(三)
这个例子实现的是客户端向服务器发送请求,服务器向客户端发送响应数据。
服务端(.net C#)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//第一个参数指定寻址方案,AddressFamily.InterNetwork代表的是ipv4寻址方案
//第二个参数指定连接类型,SocketType.Stream代表的是基于字节流的可靠双工连接
//第三个参数指定运输层协议,这里指定用tcp协议承载
Socket soc_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//指定套接字监听的IP及端口。IP是指客户端的IP,Any表示允许任何客户端接入。端口是本地端口。这样的设置意味着允许任意IP的客户端通过本机2222端口接入
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 2222);
soc_server.Bind(ip);
//10表示挂起连接队列的最大长度是10
soc_server.Listen(10);
while (true)
{
Console.WriteLine(soc_server.LocalEndPoint.ToString() + "等待客户端连接");
//当有连接接入时,创建新套接字为这个连接服务。我们将使用这个新套接字与客户端通信
//Accept方法是一个同步方法,会阻塞所在线程。也就是说,此处Accept函数调用时会停下来等待,直到有连接接入才会返回继续执行后面的代码
Socket soc_new = soc_server.Accept();
Console.WriteLine(soc_new.RemoteEndPoint.ToString() + "已连接");
string all = string.Empty;
while (true)
{
string input = string.Empty;
//建立输入缓存
byte[] receiveBytes = new byte[1024];
//每次读取1024个字节,直到读到指定的字符串,一次读取结束。
//如果没有可读取的数据,则 Receive 方法将一直处于阻止状态
//如果远程主机使用 Shutdown 方法关闭了 Socket 连接,并且所有可用数据均已收到,则 Receive 方法将立即完成并返回零字节。
int byteNums = soc_new.Receive(receiveBytes);
if (byteNums == 0)
{
Console.WriteLine("远程主机主动关闭");
break;
}
//编码数据
input = Encoding.ASCII.GetString(receiveBytes, 0, byteNums);
all += input;
Console.WriteLine("收到:" + input);
//当接收到的"[FINAL]"时,会话结束
if (input.IndexOf("[final]") > -1)
{
Console.WriteLine("收到结束字符串");
break;
}
}
byte[] reply = Encoding.ASCII.GetBytes(all + ":receive succeed");
soc_new.Send(reply);
//禁止客户端收发数据
soc_new.Shutdown(SocketShutdown.Both);
soc_new.Close();
}
soc_server.Close();
}
}
}
客户端(Android)
/MyQQ/res/drawable-hdpi/sharp_liaotian.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 外框 -->
<stroke
android:width="2dp"
android:color="#DD2ECCFA"
/>
<!-- 内容 -->
<solid
android:color="#ffffffff"
/>
<!-- 拐角 -->
<corners
android:radius="5dp"
/>
</shape>
/MyQQ/res/layout/main.xml文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffffff"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_vertical"
android:background="@drawable/app_logo" >
</ImageView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingLeft="20dp"
android:text="与XXX的聊天"
android:textSize="20dp" >
</TextView>
</LinearLayout>
<TextView
android:id="@+id/txt_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="7"
android:background="@drawable/sharp_liaotian"
android:padding="5dp"
android:text="聊天记录"
android:textColor="#DD2ECCFA" >
</TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:layout_weight="1"
android:orientation="horizontal" >
<EditText
android:id="@+id/edt_input"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:layout_weight="3"
android:background="@drawable/sharp_liaotian"
android:gravity="center"
android:hint="请输入聊天內容"
android:textColor="#4F2F4F" >
</EditText>
<Button
android:id="@+id/btn_send_message"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:layout_weight="1"
android:gravity="center"
android:text="发送"
android:textColor="#DD2ECCFA" />
</LinearLayout>
</LinearLayout>
/MyQQ/src/com/yongyu/myqq/IConnectSuccess.java文件:
package com.yongyu.myqq;
import java.net.Socket;
public interface IConnectSuccess
{
public void onGetSocket(Socket s);
}
/MyQQ/src/com/yongyu/myqq/IGetMessage.java文件:
package com.yongyu.myqq;
public interface IGetMessage
{
public void onGetMessage(String str);
}
/MyQQ/src/com/yongyu/myqq/MainActivity.java文件:
package com.yongyu.myqq;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements IConnectSuccess,
IGetMessage
{
private Socket connect;
private EditText edt_input;
private ArrayBlockingQueue queue;
private TextView txt_message;
private Handler handler = null;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn_send_message = (Button) findViewById(R.id.btn_send_message);
// 每个Handler实例,都会绑定到创建他的线程中。Handler实例可以分发Message对象和Runnable对象其绑定的线程中并伺机执行
handler = new Handler();
edt_input = (EditText) findViewById(R.id.edt_input);
txt_message = (TextView) findViewById(R.id.txt_message);
btn_send_message.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
if (connect == null)
{
Toast t = Toast.makeText(MainActivity.this, "网络异常,请检查设置!",
2000);
t.show();
return;
}
if (edt_input.getText().toString().equals(""))
{
Toast t = Toast.makeText(MainActivity.this, "请输入消息!", 2000);
t.show();
return;
} else
{
String str = edt_input.getText().toString();
try
{
MainActivity.this.queue.put(str);
} catch (InterruptedException e)
{
Log.e("com.yongyu.myqq.MainActivity.onCreate",
e.getMessage());
}
txt_message.setText(txt_message.getText() + "\n发出的信息是:"
+ str);
edt_input.setText("");
}
}
});
Task_Connect task_conn = new Task_Connect(this);
task_conn.execute();
}
@Override
public void onGetSocket(Socket s)
{
this.connect = s;
this.queue = new ArrayBlockingQueue<String>(10);
Thread t_s = new Thread(new MyRunnableSendMessage(connect, queue));
t_s.start();
Thread t_r = new Thread(new MyRunnableReceiveMessage(connect, this));
t_r.start();
}
@Override
public void onGetMessage(String str)
{
myUpdateUIRunnable runnable = new myUpdateUIRunnable(str, txt_message);
handler.post(runnable);
}
}
class myUpdateUIRunnable implements Runnable
{
private String msg;
private TextView text;
public myUpdateUIRunnable(String str, TextView t)
{
msg = str;
text = t;
}
@Override
public void run()
{
text.setText(text.getText() + "\n收到的信息是:" + msg);
}
}
/MyQQ/src/com/yongyu/myqq/MyRunnableReceiveMessage.java文件:
package com.yongyu.myqq;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketException;
public class MyRunnableReceiveMessage implements Runnable
{
private Socket socket;
private IGetMessage iget;
private String message = "";
public MyRunnableReceiveMessage(Socket _socket, IGetMessage _get)
{
this.socket = _socket;
this.iget = _get;
}
@Override
public void run()
{
InputStream in = null;
if (!isConnected())
return;
try
{
// 尝试打开输入流
in = this.socket.getInputStream();
} catch (IOException e1)
{
return;
}
while (true)
{
try
{
if (!isConnected())
{
break;
}
// 读取消息-------------------------
InputStreamReader inReader = new InputStreamReader(
socket.getInputStream());
BufferedReader reader = new BufferedReader(inReader);
message = reader.readLine();
// --------------------------------
// 直接在异步线程里更新UI会导致:Only the original thread that created a view
// hierarchy can touch its views.
if (message == null)
continue;
this.iget.onGetMessage(message);
message = "";
} catch (SocketException e)
{
// 如果本地套接字关闭,退出输入尝试
if (e.getMessage().toLowerCase().trim()
.equals(("Socket is closed").toLowerCase().trim()))
break;
} catch (Exception e0)
{
// 继续输入尝试
continue;
}
}
try
{
socket.shutdownOutput();
} catch (IOException e)
{
}
}
private boolean isConnected()
{
// 注意,在套接字编程中,我们要时刻注意对网络连接状态进行判断,java中的Socket提供的isClosed(),isConnected(),isInputShutdown(),sOutputShutdown()都是用于检查本地Socket状态的,不是用于检查远程Socket状态的,所以是无效的的。
// 要检查远程Socket的状态可以用:
try
{
socket.sendUrgentData(0xFF);
return true;
} catch (Exception e)
{
return false;
}
}
}
/MyQQ/src/com/yongyu/myqq/MyRunnableSendMessage.java文件:
package com.yongyu.myqq;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ArrayBlockingQueue;
public class MyRunnableSendMessage implements Runnable
{
private Socket socket;
private ArrayBlockingQueue queue;
public MyRunnableSendMessage(Socket _socket, ArrayBlockingQueue _que)
{
this.socket = _socket;
this.queue = _que;
}
@Override
public void run()
{
OutputStream out;
if (!isConnected())
return;
try
{
// 尝试打开输出流
out = this.socket.getOutputStream();
} catch (IOException e1)
{
return;
}
while (true)
{
try
{
String message = (String) queue.take();
if (!isConnected())
{
break;
}
out.write((message).getBytes());
} catch (SocketException e)
{
// 如果本地套接字关闭,退出输出尝试
if (e.getMessage().toLowerCase().trim()
.equals(("Socket is closed").toLowerCase().trim()))
break;
} catch (Exception e0)
{
// 继续输出尝试
continue;
}
}
try
{
// 对于在socket上建立的输入(输出)流一旦调用close函数关闭,会造成socket关闭。这样基于socket的输出(输入)流也将不可用
// 调用socket.shutdownOutputStream()只会单方面关闭输出流,不会导致socket关闭,此时基于socket的输入流还是可用的
socket.shutdownOutput();
} catch (IOException e)
{
}
}
private boolean isConnected()
{
// 注意,在套接字编程中,我们要时刻注意对网络连接状态进行判断,java中的Socket提供的isClosed(),isConnected(),isInputShutdown(),sOutputShutdown()都是用于检查本地Socket状态的,不是用于检查远程Socket状态的,所以是无效的的。
// 要检查远程Socket的状态可以用:
try
{
socket.sendUrgentData(0xFF);
return true;
} catch (Exception e)
{
return false;
}
}
}
/MyQQ/src/com/yongyu/myqq/Task_Connect.java文件:
package com.yongyu.myqq;
import java.net.Socket;
import android.os.AsyncTask;
import android.util.Log;
public class Task_Connect extends AsyncTask<Void, Void, Socket>
{
private IConnectSuccess iget;
public Task_Connect(IConnectSuccess _iget)
{
this.iget = _iget;
}
@Override
protected Socket doInBackground(Void... arg0)
{
Socket connect = null;
try
{
connect = new Socket("192.168.10.111", 2222);
} catch (Exception e)
{
connect = null;
Log.e(" com.yongyu.myqq.Task_Connect.doInBackground",
e.getMessage());
}
return connect;
}
@Override
protected void onPostExecute(Socket result)
{
this.iget.onGetSocket(result);
}
}
效果:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。