Socket通信
PC与PC,PC与Android的Socket通信
本程序已经本人虚拟机,实体机验证。Android版本在4.0以上。
本人是初学者,因为喜欢业余做游戏,涉及到socket通信,参考许多资料和网上各位大神传的程序,还是遇到许多bug,经过一点点的查找整理,调试,分析,终于通信成功。本文服务端是一对多通信,使用本人的PC机做服务端,客户端分别用:本人PC机,Android模拟器(4.4.2),以及公司用的mid(android系统的平板电脑),
需要注意的地方:服务端采用线程来处理每个客户端的通信,客户端(尤其是Android4.0以上版本)采用线程来发送接收数据与服务端通信(刚开始没有采用线程,直接搬网上一些大神的程序,他们许多是在2.3版本做的,在4.0以上版本总是出问题)。另外需要注意:控件不能在子线程中修改,按键监控,线程判断等。
下面贴出程序:
PC机主控端:
a.主程序
importjava.io.IOException;
import java.net.ServerSocket;
importjava.net.Socket;
public class MultiServer {
public static double area=0;
public MultiServer() {
// TODOAuto-generated constructor stub
}
public static void main(String[] args) {
// TODOAuto-generated method stub
try{
System.out.println("服务器启动了");
ServerSocket serverSocket = new ServerSocket(12345);
Socket connectToClient = null;
while(true){//这是一个无限循环
//等待客户请求
connectToClient =serverSocket.accept();
//
new ServerThread(connectToClient);
}
}catch(IOException e){
e.printStackTrace();
}
}
}
b.线程处理程序:
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.IOException;
importjava.net.Socket;
public class ServerThread extends Thread {
private Socket connectToClient;//声名套接字
private DataInputStream inFromClient;//声名数据输入流
private DataOutputStream outToClient;//声名数据输出流
//private double area=0;
public ServerThread(Socket socket) throws IOException {
// TODOAuto-generated constructor stub
super();
connectToClient = socket; //实例化
inFromClient = newDataInputStream(connectToClient.getInputStream());
outToClient = new DataOutputStream(connectToClient.getOutputStream());
start();//启动run()方法
}
public void run(){
String str;
double radius=0;//area=0;
boolean goon = true;
try{
System.out.println("有客户端连接到服务器了");
while(goon){
str = inFromClient.readUTF();
if(!str.equals("bye")){
if(str.equals("接受")){
str = inFromClient.readUTF();
radius = Double.parseDouble(str);
System.out.println("接收到的半径值为:" +radius);
MultiServer.area = radius*radius*Math.PI;
str = Double.toString(MultiServer.area);
System.out.println("圆面积是"+str);
outToClient.writeUTF("我已经准备好了,快来接收吧");
outToClient.flush();
System.out.println("连接服务器成功了");
}
//str = inFromClient.readUTF();
else if(str.equals("发送")){
System.out.println("要发送的圆面积是:" +MultiServer.area);
str = Double.toString(MultiServer.area);
outToClient.writeUTF(str);
outToClient.flush();
System.out.println("圆面积"+str+"已经发送");
System.out.println("连接服务器成功了");
}
}else{
goon = false;
outToClient.writeUTF("bye");
outToClient.flush();
System.out.println("连接服务器结束了");
}
}
inFromClient.close();
outToClient.close();
connectToClient.close();
}catch(IOException e){
e.printStackTrace();
}
}
public ServerThread(Runnable target) {
super(target);
// TODOAuto-generated constructor stub
}
public ServerThread(String name) {
super(name);
// TODOAuto-generated constructor stub
}
public ServerThread(ThreadGroup group, Runnable target) {
super(group, target);
// TODOAuto-generated constructor stub
}
public ServerThread(ThreadGroup group, String name) {
super(group, name);
// TODOAuto-generated constructor stub
}
public ServerThread(Runnable target, String name) {
super(target, name);
// TODOAuto-generated constructor stub
}
public ServerThread(ThreadGroup group, Runnable target, String name) {
super(group, target, name);
// TODOAuto-generated constructor stub
}
public ServerThread(ThreadGroup group, Runnable target, String name,
long stackSize) {
super(group, target, name, stackSize);
// TODOAuto-generated constructor stub
}
}
2.pc机客户端:
importjava.io.BufferedReader;
importjava.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.IOException;
importjava.io.InputStreamReader;
import java.net.Socket;
public class Client {
public Client() {
// TODOAuto-generated constructor stub
}
public static void main(String[] args) {
// TODOAuto-generated method stub
try{
//连接到本机,端口号是5500
Socket connectToServer = new Socket("localhost",12345);
//将数据输入流连接到socket
DataInputStream inFromServer = new DataInputStream(
connectToServer.getInputStream());
//将数据输出流连接到socket
DataOutputStream outToServer = new DataOutputStream(
connectToServer.getOutputStream());
System.out.println("输入半径数值发送到服务器,输入bye结束。");
String outStr,inStr;
boolean goon = true;
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
//反复读用户数据并计算
while(goon){
outToServer.writeUTF("接受");
outToServer.flush();
outStr = buf.readLine();//读入用户输入
outToServer.writeUTF(outStr);
outToServer.flush();
inStr = inFromServer.readUTF();
System.out.println("收到的握手信号是: "+inStr);
if(inStr.equals("我已经准备好了,快来接收吧"))
{
//System.out.println("程序走到这一步了吗?");
outToServer.writeUTF("发送");
outToServer.flush();
inStr = inFromServer.readUTF();
}
if(!inStr.equals("bye")){
System.out.println("从服务器返回的结果是: "+inStr);
}else{
goon = false;
System.out.println("从服务器返回的结果结束! ");
}
}
inFromServer.close();
outToServer.close();
connectToServer.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
3.Android客户端
a.Main.xml:
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<Button
android:id="@+id/rev"
android:text="接收显示"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/out"
android:text="获取发送"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入半径值:"
/>
<!--android:text="请输入半径值:" -->
<EditText
android:id="@+id/display"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请查看圆面积为"
/>
<!--android:text="圆面积为:" -->
<TextView
android:id="@+id/receiver"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="IsTransproting...." />
<!-- <TextView
android:id="@+id/outputer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="IsTransporting...." /> -->
</LinearLayout>
b.Mainfest
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rocketandclient"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="19"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="com.example.rocketandclient.Main"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
主要是开机网络通信:<uses-permission android:name="android.permission.INTERNET"/>
C.Main.class
packagecom.example.rocketandclient;
import java.io.DataInputStream;
importjava.io.DataOutputStream;
importjava.io.IOException;
import java.io.InputStream;
importjava.net.InetAddress;
importjava.net.Socket;
importjava.net.UnknownHostException;
importcom.example.rocketandclient.R;
import com.example.rocketandclient.Main.outputerlistenr;
import com.example.rocketandclient.Main.receiverlistenr;
importandroid.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
importandroid.os.Bundle;
importandroid.os.Handler;
import android.os.Message;
importandroid.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
importandroid.view.View;
import android.view.ViewGroup;
importandroid.view.View.OnClickListener;
import android.widget.Button;
importandroid.widget.EditText;
importandroid.widget.TextView;
import android.os.Build;
/***
* 注意,以下采用两种方法实现添加线程
* @author Administrator
*
*/
public class Main extends Activity implements OnClickListener, Runnable {// //implements Runnable
private Button btnRev=null;
private Button btnOut=null;
private TextView revtext=null;
private EditText outText=null;
private EditText displayText=null;
private String str;
private DataInputStream inRec=null;
private DataOutputStream output=null;
private InetAddress serverAddr ;
private Thread th1,th2,th;
private Socket mSocket;
// public final boolean sendMessage (Message msg);
// public void handleMessage (Message msg);
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnRev=(Button)findViewById(R.id.rev);
btnOut=(Button)findViewById(R.id.out);
outText=(EditText)findViewById(R.id.show);
displayText=(EditText)findViewById(R.id.display);
revtext=(TextView)findViewById(R.id.receiver);
/*
* 注意两种按键监听的写法,一种需要执行OnClickListener接口,
* btnOut.setOnClickListener(this);
*并且重写 @Override
*public void onClick(View v)
*一种:rev.setOnClickListener(new receiverlistenr());需要写内部类:
*class receiverlistenr implements OnClickListener{
* public void onClick(View v) { ;}}
*
*/
//rev.setOnClickListener(new receiverlistenr());
//out.setOnClickListener(new outputerlistenr());
btnRev.setOnClickListener(this);
btnOut.setOnClickListener(this);
/*if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}*/
}
class receiverlistenr implements OnClickListener{
public void onClick(View v) { // TODO Auto-generated method stub
Log.v(str, "接收操作");
th2 = new Thread(new SocketInRun());
th2.start();
}
}
class outputerlistenr implementsOnClickListener{
public void onClick(View v) { // TODO Auto-generated method stub
Log.v(str, "进入发送操作");
th1 = new Thread(new SocketOutRun());
th1.start();
}
}
class SocketInRun implementsRunnable {
@Override
public void run(){
try {
//实例化Socket
Socketsocket2 = null;
serverAddr = InetAddress.getByName("192.168.9.124");
socket2=new Socket("192.168.9.124",12345);
output = new DataOutputStream(socket2.getOutputStream());
output.writeUTF("发送");
output.flush();
//获得输入流
inRec=new DataInputStream(socket2.getInputStream());
String msg="";
str=msg = inRec.readUTF();
Log.v(str,"接收到的圆面积是:msg"+ str);
//设置文本框的字符串
//displayText.setText(msg);
//Main.revtext.setText(msg);//子线程内不能设置控件,否则程序崩溃
Log.v(msg,"接收到的圆面积是:"+msg);
inRec.close();
socket2.close();
}
catch (UnknownHostException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
catch (IOException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
}
}
class SocketOutRun implementsRunnable {
@Override
public void run(){
try {
//实例化Socket
Socketsocket1 = null;
serverAddr = InetAddress.getByName("192.168.9.124");
Log.v(str, "进入发送操作1");
socket1=new Socket("192.168.9.124",12345);
Log.v(str, "进入发送操作2");
Stringmsg = outText.getText().toString();
output = new DataOutputStream(socket1.getOutputStream());
output.writeUTF("接受");
output.flush();
output.writeUTF(msg);
output.flush();
output.close();
socket1.close();
}
catch (UnknownHostException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
catch (IOException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
} //catch(Exceptione){}
}
@Override
public void onClick(View v) {
// TODOAuto-generated method stub
if(v == btnRev){
Log.v(str, "接收操作");
//th2 = new Thread(new SocketInRun());
th2 = new Thread(this);//线程实例化
th2.start();//线程启动
//displayText.setText(str);
//revtext.setText(str);
}
if(v == btnOut){
Log.v(str, "进入发送操作");
//th1 = new Thread(new SocketOutRun());
th1 = new Thread(this);
th1.start();
}
}
@Override
public void run() {
// TODOAuto-generated method stubthis
th = new Thread(this);
if(th.currentThread() == th1){//判断当前执行的是哪个线程,如果是线程1,操作发送程序
Log.v(str, "1");
try {
//实例化Socket
Socket socket1 = null;
serverAddr = InetAddress.getByName("192.168.9.124");
Log.v(str, "进入发送操作1");
socket1=new Socket("192.168.9.124",12345);
Log.v(str, "进入发送操作2");
String msg = outText.getText().toString();
output = new DataOutputStream(socket1.getOutputStream());//实例化输出流
output.writeUTF("接受");//写数据
output.flush();//
output.writeUTF(msg);
output.flush();
output.close();
socket1.close();
}
catch (UnknownHostException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
catch (IOException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
}
if(th.currentThread() == th2){//如果是进程2则操作下面接收到数据
Log.v(str, "2");
try {
//实例化Socket
Socket socket2 = null;
String msg="";
serverAddr = InetAddress.getByName("192.168.9.124");
socket2=new Socket("192.168.9.124",12345);//设置机器所用的网络IP,要与PC机保持一致,端口自定义
output = new DataOutputStream(socket2.getOutputStream());
output.writeUTF("发送");
output.flush();
//获得输入流
inRec=new DataInputStream(socket2.getInputStream());
str=msg = inRec.readUTF();//读数据
// msg = inRec.readUTF();
Message msgg = new Message();
msgg.what = 100;
this.mHandler.sendMessage(msgg);
Log.v(msg,"接收到的圆面积是:"+msg);
inRec.close();
socket2.close();
}
catch (UnknownHostException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
catch (IOException e) { // TODOAuto-generated catch block
e.printStackTrace();
}
}
}
/*
* 注意控件的设置不能在子线程中操作,需要在主线程中。
* 需要子线程中参数来改变控件的请注意以下方法
*
*/
privateHandler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what){
case 100:
displayText.setText(str);
revtext.setText(str);
break;
}
}
};
}