socket编程是网络通信的一个基础应用。不管是手机端还是PC端都须要socket技术来建立网络通信。
在本章小编主要从下面几个方面来介绍socket的相关知识:
各自是“什么是socket?”,“socket有什么特点?”,“socket与Http以及TCP的差别”。“移动端socket的Demo”。写的不好的地方请大家批评指正。
一、何为socket?
用于在client和服务端之间建立一个通信管道,使得应用程序/client能够通过这个通信管道向server/还有一台主机发送请求,同一时候server也能够进行对应,建立两者之间的传输数据与交换。
在Android中,我们知道每一个应用程序都能够使用多线程来进行操作,用于网络传输的app的多个线程能够都建立一个socket连接。每一个线程维护自己的socket管道,从而实现并发的网络通信。
二、socket的特点及通信原理
TCP是可靠的而UDP是不可靠的,原因就是TCP建立时须要三次握手。两方建立通信管道才干够传输数据,且是双向的。
而UDP在传输数据时仅仅是把应用层发来的数据进行打包而且附上目的的地址,不负责对方是否接收到。
而client方面是先发送输出流,再接收到输入流。
三、socket与HTTP、TCP的差别
从上节已经了解到socket的连接是从server监听開始的,而在TCP方面,client与server的连接须要经过三次握手之后才正式開始数据的传输,而HTTP则是基于"请求-响应"才干建立传输数据,什么意思呢?仅仅有先请求,server才干对外发数据,不能主动建立传输。在HTTP经过了0.9、1.0和1.1时代,这样的连接模式能够在一次TCP连接建立时一直保持。响应多次请求。
四、Demo
我们的Demo主要功能是在编辑框中编写一段文字,然后按发送button。最后内部实现通过socket的通信方式。反馈到textview上进行显示编辑的内容。里面的细节重点会在末尾处分析。
android:id="@+id/btn_conn"/>
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="请在这里编辑要发送的内容"
android:id="@+id/et_message"/>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="发送"
android:id="@+id/btn_sent"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/tv"
android:hint="接收发来的数据"/>
</LinearLayout>
布局效果例如以下:
中文能够使用GB2312.
package com.example.mysocketdemo;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import android.os.Build;
public class MainActivity extends Activity {
private TextView tv;
private Button btnsent,btnconn;
private EditText ed_message;
private OutputStream output;
private Socket clientSocket;
private Handler mHandler;
private MyThread mythread;
boolean stop = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
init();
//onClickEvent---connect
btnconn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
try {
clientSocket = new Socket("127.0.0.1", 8888);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "connect ok", Toast.LENGTH_SHORT).show();
//把socket绑定到自己的线程对象
try {
mythread = new MyThread(clientSocket);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mythread.start();//启动线程
//更新UI,大部分的数据工作已经交给了mythread对象
btnsent.setEnabled(true);
btnconn.setEnabled(false);
stop = false;
}
});
//sent Message
btnsent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
//当点击button时,会获取编辑框中的数据,然后提交给线程
//先将发送内容进行打包
byte[] msgBuffer = null;
try {
msgBuffer = ed_message.getText().toString().getBytes("GB2312");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//打包完毕之后,加入socket的输出流对象
try {
output = clientSocket.getOutputStream();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//输出流对象将字节写入
//不管是输出流还是输入流。操作的都是字节,假设向变成字符串,须要自己构建String对象
output.write(msgBuffer);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "发送成功", Toast.LENGTH_SHORT).show();
ed_message.setText("");
}
});
//在连接和发送数据之后。接下来就是处理了,发送的数据会通过message的方式传递到消息队列,再由handl进行获取
mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
tv.setText(msg.obj.toString());
};
};
}
public void init()
{
tv = (TextView) findViewById(R.id.tv);
btnsent = (Button) findViewById(R.id.btn_sent);
ed_message = (EditText) findViewById(R.id.et_message);
btnconn = (Button) findViewById(R.id.btn_conn);
btnconn.setEnabled(true);
btnsent.setEnabled(false);
}
//自己定义线程类;
private class MyThread extends Thread{
//构建自己的socket对象,用来在线程内使用
private Socket socket;
private byte[] buf = null;
private InputStream inputstream;
String str = null;
public MyThread(Socket socket) throws IOException
{
this.socket = socket;
inputstream = this.socket.getInputStream();
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!stop)
{
buf = new byte[512];
//将inputstream内的数据读到buf里
try {
this.inputstream.read(buf);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
//将buf里的字符流进行解析,得到
this.str = new String(buf, "GB2312").trim();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//线程内获取了来自socket的Inputstream字节流,而且转换成字符串后,线程就获取了消息的实体内容
//此时线程将运行自己的一个使命。就是创建消息,并发送出去
Message msg = new Message();
msg.obj = this.str;
mHandler.sendMessage(msg);
}
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
if (mythread!=null) {
mythread.interrupt();
}
super.onDestroy();
}
当socket的getOutputStream获取了outputstream对象之后,运行了write方法进行数据的写入,而此时线程依旧在运行,它突然发现连接的socket里有数据了,就使用getIupteStream获取了inputstream对象,採用了read方法读取了字节流,最后打包成字符串。