Socket应用
前段时间在做一个用安卓app控制机器人移动的项目,利用socket与机器人通信,控制机器人的移动并且读取机器人所返回的信息,因此学习了部分的socket知识
IP地址
- 为了实现网络中不同计算机之间的通信,每个机器都有一个唯一标识——IP地址(类似于手机号)
- IP地址的格式:数字型,如192.168.0.1(本机IP)
端口号
- 用处:一台主机给另一台主机发送数据被正确的应用程序接收
- 用于区分不同的应用程序
- IP地址和端口号组成了Socket,是网络运行程序之间的双向通信链路的终结点,TCP和UDP的基础。
- 常用的端口号:http:80 ftp:21 telnet:23
JAVA的API
- InetAddress:用于表示网络上的硬件资源
- URL:统一资源定位符,通过URL可以直接读取或者写入网络上的数据
- Sockets:使用TCP协议实现网络通信Socket相关类
- Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信
InetAddress类
public class Test01 {
public static void main(String args[]) throws UnknownHostException{
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名" + address.getHostName());
System.out.println("IP地址" + address.getHostAddress());
byte[] bytes = address.getAddress(); //字节数组的形式
System.out.println(Arrays.toString(bytes));
//根据主机名获取InetAddress实例
InetAddress address1 = InetAddress.getByName("asus-PC");
System.out.println("IP地址" + address1.getHostAddress());
}
}
URL类
public class URLTest {
public static void main(String args[]){
try{
//创建一个Url的实例
URL imooc = new URL("http://www.imooc.com");
//问号后面表示参数,#后面指示锚点
URL url = new URL(imooc,"/index.html?username=tom#test");
System.out.println("协议:"+ url.getProtocol());
System.out.println("主机:"+url.getHost());
//未指定端口号。则使用默认的端口,此时getPort返回-1
System.out.println("端口号"+url.getPort());
} catch (MalformedURLException ex){
ex.printStackTrace();
}
//读取网络上的资源
try{
URL url1 = new URL("http://www.baidu.com");
InputStream in = url1.openStream();
//字节输入流转换成字符输入流,并且注意编码
InputStreamReader inr = new InputStreamReader(in,"utf-8");
//添加缓冲
BufferedReader br = new BufferedReader(inr);
String data = br.readLine();
while (data != null){
System.out.println(data);
data = br.readLine();
}
//关闭
br.close();
inr.close();
in.close();
}catch (MalformedURLException ex){
ex.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket编程
步骤
- 创建SeverSocket 和 Socket
- 打开连接到Socket的输入流和输出流
- 按照协议对Socket进行读写操作
- 关闭输入流和输出流,关闭Socket
服务器端
- 创建SeverSocket对象,绑定监听端口
- 通过accept()方法监听客户端请求
- 连接建立后,通过输入流读客户端发送的取请求信息
- 通过输出流向客户端发送响应信息
- 关闭相关资源
客户端
- 创建Scoket对象,指明需要连接服务器的地址和端口号
- 连接建立后,通过输出流向服务器发送请求
- 通过输入流获取服务器的响应信息
- 关闭相关资源
severSocket代码示例
public class Sever {
public static void main(String args[]){
try{
ServerSocket serverSocket = new ServerSocket(8899);
System.out.println("Waiting to connect");
Socket socket = serverSocket.accept();
//获取输入流、读取客户端信息
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine() )!= null){
System.out.println(info);
}
//关闭输入流
socket.shutdownInput();
//关闭其他资源
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
}catch (IOException ex){
ex.printStackTrace();
}
}
}
client端代码示例
public class Client {
public static void main(String args[]){
try {
//指定了当前的IP地址和端口号
Socket socket = new Socket("localhost",8899);
//客户端向服务器发送信息,获取输出流发送信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os); //输出流变成打印流
pw.write("用户名: admin;密码: 123");
pw.flush();//刷新缓存向服务器端发送信息
socket.shutdownOutput();//关闭输出流
//关闭其他资源
pw.close();
os.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
解决Socket乱码的问题
在输出端设置outputStream的文本编码
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os,"utf-8");
PrintWriter pw = new PrintWriter(osw); //输出流变成打印流
pw.write("username:admin;password:123");
在SocketServe端设置接受的文本编码
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
多线程的Socket编程
创建一个Thread
public class SeverThread extends Thread {
Socket socket = null;
public SeverThread(Socket socket){
this.socket = socket;
}
public void run(){
//获取输入流、读取客户端信息
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
OutputStreamWriter osw = null;
PrintWriter pw = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is, "utf-8");
br = new BufferedReader(isr);
String info = null;
while ((info = br.readLine()) != null) {
System.out.println(info);
}
//关闭输入流
socket.shutdownInput();
//获取输出流
os = socket.getOutputStream();
osw = new OutputStreamWriter(os, "utf-8");
pw = new PrintWriter(osw);
pw.write("欢迎登陆");
pw.flush();
}catch (IOException ex){
ex.printStackTrace();
}finally {
//关闭其他资源
try {
if (pw != null) {
pw.close();
}
if (osw != null) {
osw.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 ex){
ex.printStackTrace();
}
}
}
}
创建一个Sever用死循环监听
public class Sever2 {
public static void main(String args[]) {
try {
ServerSocket serverSocket = new ServerSocket(8899);
System.out.println("Waiting to Connect");
Socket socket = null;
int count =0 ; //计算客户端的数量
while (true){
socket = serverSocket.accept();
SeverThread thread = new SeverThread(socket);
thread.start();
count++;
System.out.println(count);
InetAddress address = socket.getInetAddress();
System.out.println("当前客户端的IP" +address.getHostAddress());
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
UDP编程
UDP协议以数据报作为数据传输的载体
进行数据传输时将数据定义成数据报(Datagram),并且在数据报中指明数据所要到达的Socket(主机地址和端口号),然后把数据报发送出去
所使用的工具类
- DatagramPacket:表示数据报包
- DatagramSocket:进行端到端的通信
服务器端的实现步骤
- 创建DatagramSocket,指定端口号
- 创建DatagramPacket
- 接收客户端发送的数据信息
- 读取数据
代码
public class UDPSever {
public static void main(String args[]) throws IOException {
DatagramSocket socket = new DatagramSocket(8899);
//创建数据报用于接收客户端的数据存放在字节数组中
byte [] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data,data.length);
//接受数据
socket.receive(packet); //此方法接收到数据报前一直处于阻塞
String info = new String(data,0,packet.getLength());//起始位置和末端
System.out.println("服务器接收到:"+info);
/**
* 向客户端响应数据
*/
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte []reply = "欢迎你".getBytes();
//创建数据报
DatagramPacket packet1 = new DatagramPacket(reply,reply.length,address,port);
//响应客户端
socket.send(packet1);
socket.close();
}
}
客户端实现的流程
- 定义发送信息
- 创建DatagramPacket,包含将要发送的信息
- 创建DatagramSocket
- 发送数据
代码
public class UDPClient {
public static void main(String args[]) throws IOException {
InetAddress address = InetAddress.getByName("localhost");
int port = 8899;
byte [] data = "用户名:admin;密码:123".getBytes();
//创建一个数据报,包含服务器的端口和信息
DatagramPacket packet = new DatagramPacket(data,data.length,address,port);
//创建DatagramSocket
DatagramSocket socket = new DatagramSocket();
socket.send(packet);
//接受响应信息
byte data1[] = new byte[1024];
DatagramPacket receive = new DatagramPacket(data1,data1.length);
socket.receive(receive);
String s = new String(data1,0,receive.getLength());
System.out.println("客户端接受到服务器的信息为"+s);
socket.close();
}
}
UDP的多线程操作
总结
- 设置线程的优先级,降低
- 一般不需要关闭输入输出流,最终关闭Socket即可
- 使用TCP传输对象