一、Android 开发之Socket编程 UDP和TCP通信实现
出处:http://www.linuxidc.com/Linux/2011-08/40053.htm
1、 Socket通信模型如下:
应用程序通过套接字可以进行通信,可以使用udp协议或者使用tcp协议
当客户端和服务器端的协议时相对应的,客户端使用tcp,那么服务器端使用tcp
2、 UDP协议:把数据打包成数据包,然后直接发送对应的ip地址,速度快,但是不保证
成功率,并且数据大小有限
TCP协议:首先连接接收方,然后发送数据,保证成功率, 速度慢
3、 TCP通信方式如下:
而UDP通信不使用InputStream和OutputStream
4、 UDP通信实现:
UDP使用DatagramSocket对象来实现
UDP的客户端代码实现如下:
public static void main(String[] args) {
try {
//首先创建一个DatagramSocket对象
DatagramSocket socket = new DatagramSocket(4567);
//创建一个InetAddree
InetAddress serverAddress = InetAddress.getByName("192.168.1.104");
String str = "hello"; //这是要传输的数据
byte data [] = str.getBytes(); //把传输内容分解成字节
//创建一个DatagramPacket对象,并指定要讲这个数据包发送到网络当中的哪个、
地址,以及端口号
DatagramPacket packet = new
DatagramPacket(data,data.length,serverAddress,4567);
//调用socket对象的send方法,发送数据
socket.send(packet);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
UDP的服务端代码实现如下:
//创建一个DatagramSocket对象,并指定监听的端口号
DatagramSocket socket = new DatagramSocket(4567);
byte data [] = new byte[1024];
//创建一个空的DatagramPacket对象
DatagramPacket packet =
new DatagramPacket(data,data.length);
//使用receive方法接收客户端所发送的数据,
//如果客户端没有发送数据,该进程就停滞在这里
socket.receive(packet);
String result = new
String(packet.getData(),packet.getOffset(),
packet.getLength());
System.out.println("result--->" + result);
5、 TCP通信的实现:
TCP使用Socket对象
TCP协议客户端实现:
//创建一个Socket对象,指定服务器端的IP地址和端口号
Socket socket = new Socket("192.168.1.104",4567);
//使用InputStream读取硬盘上的文件
InputStream inputStream = new
FileInputStream("f://file/words.txt");
//从Socket当中得到OutputStream
OutputStream outputStream = socket.getOutputStream();
byte buffer [] = new byte[4*1024];
int temp = 0 ;
//将InputStream当中的数据取出,并写入到OutputStream当中
while((temp = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0, temp);
}
outputStream.flush();
}
TCP协议服务器端现实:
//声明一个ServerSocket对象
ServerSocket serverSocket = null;
try {
//创建一个ServerSocket对象,并让这个Socket在4567端口监听
serverSocket = new ServerSocket(4567);
//调用ServerSocket的accept()方法,接受客户端所发送的请求,
//如果客户端没有发送数据,那么该线程就停滞不继续
Socket socket = serverSocket.accept();
//从Socket当中得到InputStream对象
InputStream inputStream = socket.getInputStream();
byte buffer [] = new byte[1024*4];
int temp = 0;
//从InputStream当中读取客户端所发送的数据
while((temp = inputStream.read(buffer)) != -1){
System.out.println(new String(buffer,0,temp));
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
serverSocket.close();
}
二、 android之socket编程实例出处:http://blog.csdn.net/x605940745/article/details/17001641
注意点:注册访问的网络权限;Android中UI线程不能有访问网络的操作,否则会报android.os.NetworkOnMainThreadException的异常
- <uses-permission
- android:name="android.permission.INTERNET"/>
-
- Android开发联盟③ 433233634
实例一
客户端
- package com.android.xiong.simplesocket;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketTimeoutException;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.view.Menu;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- Socket socket = null;
- String buffer = "";
- TextView txt1;
- Button send;
- EditText ed1;
- String geted1;
- public Handler myHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == 0x11) {
- Bundle bundle = msg.getData();
- txt1.append("server:"+bundle.getString("msg")+"\n");
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- txt1 = (TextView) findViewById(R.id.txt1);
- send = (Button) findViewById(R.id.send);
- ed1 = (EditText) findViewById(R.id.ed1);
- send.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- geted1 = ed1.getText().toString();
- txt1.append("client:"+geted1+"\n");
- //启动线程 向服务器发送和接收信息
- new MyThread(geted1).start();
- }
- });
- }
- class MyThread extends Thread {
- public String txt1;
- public MyThread(String str) {
- txt1 = str;
- }
- @Override
- public void run() {
- //定义消息
- Message msg = new Message();
- msg.what = 0x11;
- Bundle bundle = new Bundle();
- bundle.clear();
- try {
- //连接服务器 并设置连接超时为5秒
- socket = new Socket();
- socket.connect(new InetSocketAddress("1.1.9.30", 30000), 5000);
- //获取输入输出流
- OutputStream ou = socket.getOutputStream();
- BufferedReader bff = new BufferedReader(new InputStreamReader(
- socket.getInputStream()));
- //读取发来服务器信息
- String line = null;
- buffer="";
- while ((line = bff.readLine()) != null) {
- buffer = line + buffer;
- }
- //向服务器发送信息
- ou.write("android 客户端".getBytes("gbk"));
- ou.flush();
- bundle.putString("msg", buffer.toString());
- msg.setData(bundle);
- //发送消息 修改UI线程中的组件
- myHandler.sendMessage(msg);
- //关闭各种输入输出流
- bff.close();
- ou.close();
- socket.close();
- } catch (SocketTimeoutException aa) {
- //连接超时 在UI界面显示消息
- bundle.putString("msg", "服务器连接失败!请检查网络是否打开");
- msg.setData(bundle);
- //发送消息 修改UI线程中的组件
- myHandler.sendMessage(msg);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- }
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <EditText
- android:id="@+id/ed1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="给服务器发送信息"/>
- <Button
- android:id="@+id/send"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/ed1"
- android:text="发送"/>
- <TextView
- android:id="@+id/txt1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/send"/>
- </RelativeLayout>
服务端
- package com.android.net;
- import java.io.IOException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.ArrayList;
- import java.util.List;
- public class AndroidService {
- public static void main(String[] args) throws IOException {
- ServerSocket serivce = new ServerSocket(30000);
- while (true) {
- //等待客户端连接
- Socket socket = serivce.accept();
- new Thread(new AndroidRunable(socket)).start();
- }
- }
- }
- package com.android.net;
- import java.io.BufferedReader;
- import java.io.BufferedWriter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.net.Socket;
- public class AndroidRunable implements Runnable {
- Socket socket = null;
- public AndroidRunable(Socket socket) {
- this.socket = socket;
- }
- @Override
- public void run() {
- // 向android客户端输出hello worild
- String line = null;
- InputStream input;
- OutputStream output;
- String str = "hello world!";
- try {
- //向客户端发送信息
- output = socket.getOutputStream();
- input = socket.getInputStream();
- BufferedReader bff = new BufferedReader(
- new InputStreamReader(input));
- output.write(str.getBytes("gbk"));
- output.flush();
- //半关闭socket
- socket.shutdownOutput();
- //获取客户端的信息
- while ((line = bff.readLine()) != null) {
- System.out.print(line);
- }
- //关闭输入输出流
- output.close();
- bff.close();
- input.close();
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
实例二
客户端
- package com.android.xiong.sockettwotest;
- import android.app.Activity;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.view.Menu;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- // 定义界面上的两个文本框
- EditText input;
- TextView show;
- // 定义界面上的一个按钮
- Button send;
- Handler handler;
- // 定义与服务器通信的子线程
- ClientThread clientThread;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- input = (EditText) findViewById(R.id.input);
- show = (TextView) findViewById(R.id.show);
- send = (Button) findViewById(R.id.send);
- handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // 如果消息来自子线程
- if (msg.what == 0x123) {
- // 将读取的内容追加显示在文本框中
- show.append("\n" + msg.obj.toString());
- }
- }
- };
- clientThread = new ClientThread(handler);
- // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据
- new Thread(clientThread).start();
- send.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- // 当用户按下按钮之后,将用户输入的数据封装成Message
- // 然后发送给子线程Handler
- Message msg = new Message();
- msg.what = 0x345;
- msg.obj = input.getText().toString();
- clientThread.revHandler.sendMessage(msg);
- input.setText("");
- } catch (Exception e) {
- }
- }
- });
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- }
- package com.android.xiong.sockettwotest;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.net.InetSocketAddress;
- import java.net.Socket;
- import java.net.SocketTimeoutException;
- import android.os.Handler;
- import android.os.Looper;
- import android.os.Message;
- public class ClientThread implements Runnable {
- private Socket s;
- // 定义向UI线程发送消息的Handler对象
- Handler handler;
- // 定义接收UI线程的Handler对象
- Handler revHandler;
- // 该线程处理Socket所对用的输入输出流
- BufferedReader br = null;
- OutputStream os = null;
- public ClientThread(Handler handler) {
- this.handler = handler;
- }
- @Override
- public void run() {
- s = new Socket();
- try {
- s.connect(new InetSocketAddress("1.1.9.30", 3000), 5000);
- br = new BufferedReader(new InputStreamReader(s.getInputStream()));
- os = s.getOutputStream();
- // 启动一条子线程来读取服务器相应的数据
- new Thread() {
- @Override
- public void run() {
- String content = null;
- // 不断的读取Socket输入流的内容
- try {
- while ((content = br.readLine()) != null) {
- // 每当读取到来自服务器的数据之后,发送的消息通知程序
- // 界面显示该数据
- Message msg = new Message();
- msg.what = 0x123;
- msg.obj = content;
- handler.sendMessage(msg);
- }
- } catch (IOException io) {
- io.printStackTrace();
- }
- }
- }.start();
- // 为当前线程初始化Looper
- Looper.prepare();
- // 创建revHandler对象
- revHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // 接收到UI线程的中用户输入的数据
- if (msg.what == 0x345) {
- // 将用户在文本框输入的内容写入网络
- try {
- os.write((msg.obj.toString() + "\r\n")
- .getBytes("gbk"));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- };
- // 启动Looper
- Looper.loop();
- } catch (SocketTimeoutException e) {
- Message msg = new Message();
- msg.what = 0x123;
- msg.obj = "网络连接超时!";
- handler.sendMessage(msg);
- } catch (IOException io) {
- io.printStackTrace();
- }
- }
- }
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context=".MainActivity" >
- <EditText
- android:id="@+id/input"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="@string/input" />
- <Button
- android:id="@+id/send"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/send"
- android:layout_below="@id/input"/>
- <TextView
- android:id="@+id/show"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/send"/>
- </RelativeLayout>
服务端
- package com.android.net;
- import java.io.IOException;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.ArrayList;
- import java.util.List;
- public class MyService {
- // 定义保存所有的Socket
- public static List<Socket> socketList = new ArrayList<Socket>();
- public static void main(String[] args) throws IOException {
- ServerSocket server = new ServerSocket(3000);
- while(true){
- Socket s=server.accept();
- socketList.add(s);
- //每当客户端连接之后启动一条ServerThread线程为该客户端服务
- new Thread(new ServiceThreada(s)).start();
- }
- }
- }
- package com.android.net;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.net.Socket;
- public class ServiceThreada implements Runnable {
- // 定义当前线程处理的Socket
- Socket s = null;
- // 该线程所处理的Socket所对应的输入流
- BufferedReader br = null;
- public ServiceThreada(Socket s) {
- this.s = s;
- try {
- br = new BufferedReader(new InputStreamReader(s.getInputStream()));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- @Override
- public void run() {
- String content = null;
- //采用循环不断的从Socket中读取客户端发送过来的数据
- while((content=readFromClient())!=null){
- //遍历socketList中的每个Socket
- //将读取到的内容每个向Socket发送一次
- for(Socket s:MyService.socketList){
- OutputStream os;
- try {
- os = s.getOutputStream();
- os.write((content+"\n").getBytes("gbk"));
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- // 定义读取客户端的信息
- public String readFromClient() {
- try {
- return br.readLine();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- }
三、Android socket通信(上)
今天我们介绍android下的socket通信,并编写一个小程序:android作为客户端,通过socket发送数据到我们的pc机,pc机就是服务器。
分两个实验完成:我们先在模拟器上实现,然后在真实的手机上实现。
1.
设置环境,两个实验均在ubuntu11.04下完成:
第一个实验是android模拟器作为客户端,第二个实验是真实的android手机作为客户端,两个实验的服务器都是我们的pc机,并且服务器端用c++实现,客户端用java实现:
第一个实验的ip配置:
主机eth0:192.168.1.2
pc服务器端口:9400
第二个实验的ip配置:
主机lwan0:192.168.1.100
pc服务器端口:9500
注意,第一个实验是android模拟器作为客户端,因此要设置主机的eth0的ip地址,而第二个实验是真实的android手机作为客户端,它和pc机(服务器)在一个无线路由器局域网里,因此我们要设置主机的lwan的ip地址,不过由于主机和真实手机的ip都是路由器dhcp自动分配的,因此无需额外的配置命令,你可以改成你自己的ip地址。
第一个实验的配置命令很简单:
sudo ifconfig eth0 192.168.1.2
首先介绍第一个实验:
由于模拟器的特殊性,因此我们需要将模拟器的端口映射到主机的某个端口,这样才可以和模拟器相互通信。
1.
端口映射:
在android sdk的platform-tools下有一个adb可执行程序,我的路径是android-sdk-linux_x86/platform-tools/adb,运行如下命令进行端口映射:
cd android-sdk-linux_x86/platform-tools
./adb forward tcp:9400 tcp:9400
上面命令的意思是将模拟器的9400端口映射到主机的9400端口,这样模拟器向192.168.1.2:9400发送的数据就会被映射到主机的9400端口(主机的ip地址是192.168.1.2),而我们的主机只要监听本地的9400端口即可。这里我们使用tcp socket
2.
环境配置完毕并了解了基本原理后,直接上代码,下面是客户端的代码,用java实现:
src/BogoclientActivity.java
- package bogo.client.com;
- import java.io.IOException;
- import java.io.PrintStream;
- import java.net.Socket;
- import java.net.UnknownHostException;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.Toast;
- public class BogoclientActivity extends Activity
- {
- /* 服务器地址 */
- private final String SERVER_HOST_IP = "192.168.1.2";
- /* 服务器端口 */
- private final int SERVER_HOST_PORT = 9400;
- private Button btnConnect;
- private Button btnSend;
- private EditText editSend;
- private Socket socket;
- private PrintStream output;
- public void toastText(String message)
- {
- Toast.makeText(this, message, Toast.LENGTH_LONG).show();
- }
- public void handleException(Exception e, String prefix)
- {
- e.printStackTrace();
- toastText(prefix + e.toString());
- }
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- initView();
- btnConnect.setOnClickListener(new Button.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- initClientSocket();
- }
- });
- btnSend.setOnClickListener(new Button.OnClickListener()
- {
- @Override
- public void onClick(View v)
- {
- sendMessage(editSend.getText().toString());
- }
- });
- }
- public void initView()
- {
- btnConnect = (Button)findViewById(R.id.btnConnect);
- btnSend = (Button)findViewById(R.id.btnSend);
- editSend = (EditText)findViewById(R.id.sendMsg);
- btnSend.setEnabled(false);
- editSend.setEnabled(false);
- }
- public void closeSocket()
- {
- try
- {
- output.close();
- socket.close();
- }
- catch (IOException e)
- {
- handleException(e, "close exception: ");
- }
- }
- private void initClientSocket()
- {
- try
- {
- /* 连接服务器 */
- socket = new Socket(SERVER_HOST_IP, SERVER_HOST_PORT);
- /* 获取输出流 */
- output = new PrintStream(socket.getOutputStream(), true, "utf-8");
- btnConnect.setEnabled(false);
- editSend.setEnabled(true);
- btnSend.setEnabled(true);
- }
- catch (UnknownHostException e)
- {
- handleException(e, "unknown host exception: " + e.toString());
- }
- catch (IOException e)
- {
- handleException(e, "io exception: " + e.toString());
- }
- }
- private void sendMessage(String msg)
- {
- output.print(msg);
- }
- }
layout/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" >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/hello" />
- <Button
- android:id="@+id/btnConnect"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/connect" />
- <EditText
- android:id="@+id/sendMsg"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="text" />
- <Button
- android:id="@+id/btnSend"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/send" />
- </LinearLayout>
不要忘了,在AndroidManifest.xml中添加访问网络权限:
<uses-permission android:name="android.permission.INTERNET" />
把上面的代码编译并下载到模拟器中
服务器端的代码,用c++实现:
server.c
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <stdio.h>
- #define PORT 9400
- #define MAX_BUFFER 1024
- int main()
- {
- /* create a socket */
- int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
- struct sockaddr_in server_addr;
- server_addr.sin_family = AF_INET;
- server_addr.sin_addr.s_addr = inet_addr("192.168.1.2");
- server_addr.sin_port = htons(PORT);
- /* bind with the local file */
- bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
- /* listen */
- listen(server_sockfd, 5);
- int size;
- char buffer[MAX_BUFFER + 1];
- int client_sockfd;
- struct sockaddr_in client_addr;
- socklen_t len = sizeof(client_addr);
- /* accept a connection */
- printf("waiting connection...\n");
- client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
- printf("connection established!\n");
- while(1)
- {
- printf("waiting message...\n");
- /* exchange data */
- size = read(client_sockfd, buffer, MAX_BUFFER);
- buffer[size] = '\0';
- printf("Got %d bytes: %s\n", size, buffer);
- }
- /* close the socket */
- close(client_sockfd);
- return 0;
- }
Makefile:
- all: server.c
- gcc -g -Wall -o server server.c
- clean:
- rm -rf *.o server
运行结果:
首先运行服务器代码,然后运行模拟器的bogoclient程序,如下图,pc机正等待模拟器连接,并且未连接之前模拟器的文本对话框和send按钮都是不可用的:
点击connect按钮进行连接,连接成功后我们发现文本框和send按钮可用了,connect按钮不可用了,并且主机从等待连接状态变成了等待数据状态:
输入一些文本后按send按钮,pc机就会打印出模拟器发来的文本数据:
注意,如果模拟器连接时提示Connect refused,那么把模拟器的bogoclient和pc机上的server都结束掉,然后重新开始。
代码不过多解释了,注释挺详细的,有关pc机上的tcp通信,可以参考:
http://blog.csdn.net/htttw/article/details/7519964
最后,我把这个服务器和客户端两个程序都上传上来,供大家下载:
http://download.csdn.net/detail/htttw/4307606
在下一篇里,我们要把这个程序移植到真实的android手机上了:
http://blog.csdn.net/htttw/article/details/7574409