Android socket 学习记录 之 执行new socket(ip, port)程序崩溃

这段时间在学习Android的socket编程,我不是专做APP的,做的是bootloader、驱动、hal、framework这个线的,也就是系统搭建和功能优化设计。为了打通这整条线,为此学习了不少东西,今天把Android的socket学习记录一下,以防止以后会出现这样的低级错误。

我这里是在极客学院的源码基础上做的自己的一些添加和修改,学习开始不就是先会修改么,举一反三,自然就很快学会了。由于看过视频和资料后就迫不及待的按照自己的想法想做一个功能,但是遇到麻烦了,就是执行new socket(ip, port)程序崩溃的问题,怎么找都找不到,代码也看不出问题,头大了快一天,最终还是被解决了,O(∩_∩)O。不过也庆幸找遇到这样的问题,以后就不会因为这个问题而伤脑筋了。

下面记录一下源码和相关说明。

主要使用了两个java文件。

MainActivity.java:

<pre name="code" class="java">package com.jikexueyuan.mysocketclient;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.ToggleButton;

public class MainActivity extends Activity {

	EditText ip;
	EditText port;
	EditText editText;
	TextView text;

	ToggleButton connect;
	Button send;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		ip = (EditText) findViewById(R.id.ip);
		port = (EditText) findViewById(R.id.port);
		editText = (EditText) findViewById(R.id.edit);
		text = (TextView) findViewById(R.id.text);
		send = (Button) findViewById(R.id.send);
		connect = (ToggleButton) findViewById(R.id.connect);

		connect.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent());
		send.setOnClickListener(new ButtonClickEvent());

		ConnectClass.datainit(ip, port, editText, text, send, connect);//传递布局数据给类ConnectClass
		send.setEnabled(false);
	}

	// ----------------------------------------------------清除按钮、发送按钮
	class ButtonClickEvent implements View.OnClickListener {
		public void onClick(View v) {

			if (v == send) {
				ConnectClass.send();
			}
		}
	}

	class ToggleButtonCheckedChangeEvent implements
			ToggleButton.OnCheckedChangeListener {

		@Override
		public void onCheckedChanged(CompoundButton buttonView,
				boolean isChecked) {
			if (buttonView == connect) {
				if (isChecked) {
					ConnectClass.connect(MainActivity.this);
				} else {
					ConnectClass.unconnect();
				}
			}
		}

	}
}
 

这个主文件没什么好说的,就是做一些初始化。相对于极客学院的code,我添加了ToggleButton按钮,用于连接和断开使用,另外还添加了一个端口设置窗口。布局文件也贴出来。

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <EditText
            android:id="@+id/ip"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="输入服务器的IP地址"
            android:text="192.168.0.55" >

        </EditText>

        <EditText
            android:id="@+id/port"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="0.35"
            android:hint="输入服务器的PORT"
            android:text="2030" >

        </EditText>

        <ToggleButton
            android:id="@+id/connect"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="连接"
            android:textOff="@string/connect"
            android:textOn="@string/unconnect" />
    </LinearLayout>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_weight="0.86" >

        <TextView
            android:id="@+id/text"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="Ready..." />
    </ScrollView>

    <EditText
        android:id="@+id/edit"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:hint="在这里输入内容" />

    <Button
        android:id="@+id/send"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="发送" />

</LinearLayout>


重点是ConnectClass.java这个类文件,源码如下:

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">package com.jikexueyuan.mysocketclient;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.content.Context;
import android.os.AsyncTask;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;

public class ConnectClass {
	static Socket socket = null;
	static BufferedWriter writer = null;
	static BufferedReader reader = null;
	
	static EditText ip;
	static EditText port;
	static EditText editText;
	static TextView text;
	static Button send;
	static ToggleButton connect;
	public static void datainit(final EditText tIp, final EditText tPort, 
			final EditText tEditText, final TextView tText, final Button tButton, final ToggleButton tToggleButton){
		ip = tIp;
		port = tPort;
		editText = tEditText;
		text = tText;
		send = tButton;
		connect = tToggleButton;
	}

	public static void connect(final Context tActivity) {
		AsyncTask<Void, String, Void> read = new AsyncTask<Void, String, Void>() {

			@Override
			protected Void doInBackground(Void... arg0) {
				try {	
					socket = new Socket(ip.getText().toString(), Integer.decode(port.getText().toString()));
					writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
					reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));	
//					send.setEnabled(true);
					publishProgress("@success");
				} catch (UnknownHostException e1) {
					Toast.makeText(tActivity, "无法建立链接", Toast.LENGTH_SHORT).show();
				} catch (IOException e1) {
					Toast.makeText(tActivity, "无法建立链接", Toast.LENGTH_SHORT).show();
				}
				
				try {
					String line;
					while ((line = reader.readLine())!= null) {
						publishProgress(line);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				return null;
			}

			@Override
			protected void onProgressUpdate(String... values) {
				if (values[0].equals("@success")) {
					Toast.makeText(tActivity, "链接成功!", Toast.LENGTH_SHORT).show();
					send.setEnabled(true);
				}
				text.append("别人说:"+values[0]+"\n");
				super.onProgressUpdate(values);
			}
		};
		read.execute();		
	}
	public static void unconnect() {
		try {
			writer.close();
			reader.close();
			socket.close();
			
			send.setEnabled(false);
			connect.setChecked(false);
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}
	
	public static void send() {
		try {
			text.append("我说:"+editText.getText().toString()+"\n");
			writer.write(editText.getText().toString()+"\n");
			writer.flush();
			editText.setText("");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
 

这个文件类,完成网络的连接,断开,发送等功能。

这里重点说明new socket这个函数的使用,也就是标题的崩溃问题原因。进过谷歌发现,要使用new socket这个函数,不能在主线程ui中使用,否则就会报错崩溃,必须使用分线程来调用new socket函数进行网络操作,上面用的是AsyncTask异步线程,当然其他线程也是可以的。

另外还有一个原因会导致崩溃,就是网络权限,配置文件中一定要有这句<uses-permission android:name="android.permission.INTERNET"/>。

服务端我用的是Linux系统下的,源码如下:

/* File Name: server.c */  
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
#define DEFAULT_PORT 2030  
#define MAXLINE 4096  
int main(int argc, char** argv)  
{  
    int    socket_fd, connect_fd;  
    struct sockaddr_in     servaddr;  
    char    buff[4096];  
    int     n;  
    //初始化Socket  
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
    printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    //初始化  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  
    servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  
  
    //将本地地址绑定到所创建的套接字上  
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
    printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    //开始监听是否有客户端连接  
    if( listen(socket_fd, 10) == -1){  
    printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
    printf("======waiting for client's request======\n");  
    while(1){  
//阻塞直到有客户端连接,不然多浪费CPU资源。  
        if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){  
        printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
        continue;  
    }  
//接受客户端传过来的数据  
    n = recv(connect_fd, buff, MAXLINE, 0);  
//向客户端发送回应数据  
    if(!fork()){ /*紫禁城*/  
        if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)  
        perror("send error");  
        close(connect_fd);  
        exit(0);  
    }  
    buff[n] = '\0';  
    printf("recv msg from client: %s\n", buff);  
    close(connect_fd);  
    }  
    close(socket_fd);  
}  


Makefile文件为:

关于Linux的socket,可以参考这里http://blog.csdn.net/hguisu/article/details/7445768。

发布了148 篇原创文章 · 获赞 72 · 访问量 71万+

猜你喜欢

转载自blog.csdn.net/u010406724/article/details/44834743