Socket 本质上就是 Java 封装了传输层上的 TCP 协议(注:UDP 用的是 DatagramSocket 类).要实现 Socket 的传输,需要构建客户端和服务器端.另外,传输的数据可以是字符串和字节.
基于 TCP
首先看一下通信模型:
通信模型
简单概括就是以下几个步骤:
创建 ServerSocket 和 Socket
打开连接到 Socket 的输入 / 输出流
按照协议对 Socket 进行读写操作
关闭输入输出流, 关闭 Socket
下面会通过实现一个登录的功能来介绍以上几个步骤
项目运行的时候服务器的代码放在 ServerApp 里, 客户端的代码放在 ClientApp 里, 先运行 ServerApp, 又运行 ClientApp. 两个 app 都在一个手机里面
服务器
完成以上需求可以遵循如下几个步骤:
创建 ServerSocket 对象, 绑定监听端口
通过 accept() 方法监听客户端请求
建立连接后, 通过输入流读取客户端发送的请求信息
通过输出流向客户端发送响应信息
关闭相关资源
public class MainActivity extends AppCompatActivity {@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread() {@Override public void run() {
ServerSocket serverSocket = null;
Socket socket = null;
try {
//步骤①创建ServerSocket对象,绑定监听端口
//参数port 我们要指定在1024-65535
//如果端口重复,也就是有其他的应用使用,会报异常
serverSocket = new ServerSocket(43211);
} catch(Exception e) {
e.printStackTrace();
}
int count = 0;
System.out.println("服务器端准备好了链接...");
//无线循环接受客户端的连接,也就是允许多个客户端连接,当然可以通过ServerSocket的构造方法对连接数做一个限制
//比如最大连接20,当超过20个连接后会报异常
while (true) {
try {
//步骤②通过accept()方法监听客户端请求
//这个socket是客户端与服务器通信的socket
//执行accept方法之后,服务器处于监听状态,随时与客户端连接
socket = serverSocket.accept();
} catch(IOException e) {
e.printStackTrace();
}
ServerThread serverThread = new ServerThread(socket);
//设置优先级为4(默认为5),为了保证程序运行的速度,适当降低该线程的优先级
serverThread.setPriority(4);
serverThread.start();
//记录有多少个连接
count++;
System.out.println("当前连接数" + count);
}
}
}.start();
}
}
public class ServerThread extends Thread {
Socket socket = null;
public ServerThread(Socket socket) {
this.socket = socket;
}@Override public void run() {
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
String info = null;
StringBuilder stringBuilder = new StringBuilder();
//步骤③建立连接后,通过输入流读取客户端发送的请求信息
//获取输入流
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
while ((info = br.readLine()) != null) {
stringBuilder.append(info);
}
socket.shutdownInput();
/* try {
Thread.sleep(6*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
String[] split = stringBuilder.toString().split(":");
String name = split[0];
String password = split[1];
//步骤④通过输出流向客户端发送响应信息
os = socket.getOutputStream();
pw = new PrintWriter(os);
//模拟验证账号密码是否正确
if ("admin".equals(name) && "123456".equals(password)) {
pw.write("login success!");
} else {
pw.write("login failed!!!");
}
pw.flush();
} catch(IOException e) {
e.printStackTrace();
} finally {
//步骤⑤关闭相关资源
try {
if (pw != null) pw.close();
if (os != null) os.close();
if (br != null) br.close();
if (isr != null) isr.close();
if (is != null) is.close();
if (socket != null) socket.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
}
客户端
客户端布局:
需要以下几个步骤:
创建 Socket 对象, 指明需要连接的服务器的地址和端口号
连接建立后, 通过输出流向服务器发送请求信息
通过输入流获取服务器的响应信息
关闭相关资源
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_name;
private EditText et_password;@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.bt_con).setOnClickListener(this);
et_name = findViewById(R.id.et_name);
et_password = findViewById(R.id.et_password);
}@Override public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_con:
final String name = et_name.getText().toString();
final String password = et_password.getText().toString();
if (name == null || TextUtils.isEmpty(name)) {
Toast.makeText(this, "用户名不能为空", Toast.LENGTH_SHORT).show();
return;
}
if (password == null || TextUtils.isEmpty(password)) {
Toast.makeText(this, "密码不能为空", Toast.LENGTH_SHORT).show();
return;
}
new Thread() {@Override public void run() {
try {
//步骤①创建Socket对象,指明需要连接的服务器的地址和端口号
//host指的是服务器的地址,因为服务器和客户端装在一个手机上,
Socket socket = new Socket("localhost", 43211);
//步骤②连接建立后,通过输出流向服务器发送请求信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
//写入用户名和密码
pw.write(name);
//用:来分割用户名和密码
pw.write(":");
pw.write(password);
pw.flush();
socket.shutdownOutput();
/*//注意!!!!
//上面为了简单写用的是字符串,实际开发中用的对象较多,也就是如下写法
ObjectOutputStream oos = new ObjectOutputStream(os);
User user = new User(name,password);
oos.writeObject(user);*/
//设置超时时间,如果在5s没没有收到服务器的返回,则异常
socket.setSoTimeout(1000 * 5);
//步骤③通过输入流获取服务器的响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while ((info = br.readLine()) != null) {
System.out.println(info);
}
//步骤④关闭相关资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
} catch(UnknownHostException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
}
}.start();
break;
}
}
}
来源: http://www.jianshu.com/p/457b24e5e0a5