? ? Socket(套接字)是对 TCP/IP 协议的封装和应用,根据底层封装协议的不同,Socket 的类型可以分为流套接字(streamsocket)和数据报套接字(datagramsocket)两种。流套接字将TCP作为端对端协议,提供了一个可信赖的字节流服务;数据报套接字使用 UDP 协议,提供数据打包发送服务,应用程序可以通过它发送最长 64KB 的信息。Socket 的通信模型图如1-1所示。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图1-1 Socket 的通信模型图?
? ? 通过图1-1可以很容易地看出,使用Socket进行两个应用程序之间的通信时可以选择使用TCP还是UDP作为其底层协议。对比两种方式,就会发现它们各有优劣,TCP 首先连接接收方,然后发送数据,保证成功率,速度相对较慢(相比 HTTP 方式还是非常快的);UDP 把数据打包成数据包,然后直接发送对应的IP 地址,速度快,但是不保证成功率,并且数据大小有限。
? ? 一个功能齐全的 Socket,都要包含以下基本结构,其工作过程包含4个基本的步骤∶创建Socket,打开连接到 Socket 的输入/出流,按照一定的协议对 Socket进行读/写操作,关闭 Socket。
? ? Java在java.net包中提供了 Socket 和 ServerSocket 两个类,分别用来表示双向连接的客户端和服务端,是Socket编程的核心类。构造方法很多,一般情况下使用下面两种∶
Socket client = new Socket("127.0.0.1 ",999);
ServerSocket server = new ServerSocket(999);
? ? 其中,Socket 类用于实例化一个 Client,参数分别是要访问的IP 地址和端口号,这个端口号要与服务端一致。ServerSocket类用于实例化一个 Server,其中的参数用来设置端口这里的端口不能与"3306""80""8080"等常用端口号冲突。
? ? 下面以基于TCP的Socket为例来讲解如何使用Socket。
? ? 使用Java Socket创建一个服务端程序,运行在eclipse平台,并运行在PC上,然后在手机上编写客户端程序,在局域网内访问服务端。下面先编写服务端,代码如下:
package socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class sockettest {
public static void main(String[] args) throws IOException {
@SuppressWarnings("resource")
ServerSocket service = new ServerSocket(2226);
while (true) {
//等待客户端连接
Socket socket = service.accept();
new Thread(new AndroidRunable(socket)).start();
}
}
}
class AndroidRunable implements Runnable{
Socket socket=null;
public AndroidRunable(Socket socket)
{
this.socket=socket;
}
public void run(){
//向Android客户端输出hello this is www.bigbirdic.com!
String line=null;
InputStream inputStream;
OutputStream outputStream;
String str="hello this is www.bigbirdic.com!";
try{
//向客户端发送信息
outputStream=socket.getOutputStream();
inputStream=socket.getInputStream();
BufferedReader bfr=new BufferedReader(new InputStreamReader(inputStream));
outputStream.write(str.getBytes("gbk"));
outputStream.flush();
//半关闭Socket
socket.shutdownOutput();
//获取客户端的信息
while ((line=bfr.readLine())!=null){
System.out.print(line);
}
//关闭输入输出流
outputStream.close();
bfr.close();
inputStream.close();
socket.close();
}catch (IOException e)
{
e.printStackTrace();
}
}
}
? ? 这里的代码很简单,单纯地使用ServerSocket建立服务,设置端口号为2226,然后每当有客户端访问时就返回一个“hello this is www.bigbirdic.com!”。编辑完成服务端之后,我们在Android?Studio中创建一个用于创建Socket客户端的类,代码如下:
package com.rfstar.sockettest;
import android.util.Log;
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.SocketException;
public class SocketUtil {
private String str;
private Socket socket;
private String ip;
public SocketUtil(String str,String ip){
this.str=str;
this.ip=ip;
}
public String sendMessage(){
String result="";
try{
socket=new Socket();
//ip为电脑所在的局域网网址
socket.connect(new InetSocketAddress(ip,2226),5000);
OutputStream outputStream=socket.getOutputStream();
outputStream.write(str.getBytes());
outputStream.flush();
BufferedReader bfr=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
StringBuffer buffer=new StringBuffer();
while ((line=bfr.readLine())!=null)
{
buffer.append(line);
}
result=buffer.toString();
bfr.close();
outputStream.close();
socket.close();
}catch (SocketException e)
{
//连接超时,在UI界面显示消息
Log.i("socket",e.toString());
}catch (IOException e)
{
e.printStackTrace();
}
return result;
}
}
? ? 在本类中,使用Socket连接服务器端,然后发送相关信息并接收服务端数据。代码并不难,下面就在MainActivity中使用此类。当然,使用它之前要先修改MainActivity的布局文件activity_main.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="50dp"
android:textSize="24sp"/>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="发送消息"
android:textSize="20dp"/>
<TextView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
/>
</LinearLayout>
? ? 这里使用一个EditText来编辑向服务端发送的内容,并用一个Button来点击触发网络通信的事件,然后用一个TextView展示服务端返回的值。接下来在MainActivity中通过findViewById()方法来获取这些控件,并设置Button的点击事件,代码如下:
package com.rfstar.sockettest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.w3c.dom.EntityReference;
public class MainActivity extends AppCompatActivity {
private Button button;
private TextView textView;
private String result;
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
if(msg.what==123)
{
textView.setText(result);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView()
{
textView=(TextView)findViewById(R.id.content);
final EditText editText=(EditText)findViewById(R.id.message);
button=(Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
result=new SocketUtil(editText.getText().toString(),"192.168.110.82").sendMessage();
handler.sendEmptyMessage(123);
}
}).start();
}
});
}
}
至此,实例代码完成,不要忘了还要在AndroidManifest.xml中申请权限:
<uses-permission android:name="android.permission.INTERNET"/>
? ? 在Activity中只是对点击事件做了处理,并将服务端返回的值展示在TextView上。添加完网络权限之后,运行程序,在EditText中输入内容,然后点击“发送消息”按钮,将“hello”发送到服务端,并接收到服务端返回的“hello this is www.bigbirdic.com!”,如图1-2所示。
观察服务端代码所在的控制台,发现也确实接收到了手机发送的内容,如图1-3所示。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图1-2?Socket的通信:客户端发送并接收响应的消息
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?图1-3?Socket通信:服务端接收到消息?
源码下载地址:链接:https://pan.baidu.com/s/1ExoSxaQRdjmwUUnDuSbUIQ 提取码:43ka
|