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;
}
}
}
下一篇,基于UDP:https://www.jianshu.com/p/20b2d7688cb7
网友评论