一、网络编程三要素
- IP地址:唯一识别计算机、设备
- 端口:唯一识别计算机中的应用程序,取值范围是0~65535,且普通应用程序需使用1024以上的端口号,因为0~1023用于一些知名的网络服务和系统应用
- 协议:位于同一个网络中计算机在进行连接和通信时需要遵守一定的规则,这些规则被称为网络通信协议,他对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
二、TCP与UDP协议
- UDP协议:用户数据报协议
UDP是面向无连接的通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机想另外一台计算机发送数据时,发送端不会确认接收端是否存在,直接发送数据,同理,接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通(不重要的)数据的传输。
例如视频会议通常采用UDP协议,因为这种情况即时偶尔丢失了一些数据包,也不会对整个结果产生太大影响。但是使用UDP协议传输数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。
- TCP协议:传输控制协议
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端需建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务器端发出连接请求,且每次连接的创建都需要经过“三次握手”。
完成三次握手,并建立连接后,客户端和服务端就可以开始传输数据了。由于这种面相连接的特性,TCP协议可以保证数据传输的安全,所以应用十分广泛,如上传文件、下载文件、浏览网页等。
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。 - 第一次握手:客户端向服务器发送连接请求,等待服务器确认
- 第二次握手:服务器收到请求,并向客户端回送一个响应,通知客户端收到了请求
- 第三次握手:客户端向服务端发送确认信息,以确认连接
三、InetAddress类
方便我们对IP地址的获取和操作,此类表示Internet协议(IP)地址。
- static InetAddress getByName(String
host):确定主机名称host的IP地址。主机名称可以是机器名称,也可以是IP地址(此为静态方法,采用类名调用) - String getHostName():获取此IP地址的主机名
- String getHostAddress():返回文本显示中的IP地址字符串
InetAddress address = InetAddress.getByName("192.168.100.162");
System.out.println(address.getHostAddress());
System.out.println(address.getHostName());
四、UDP编程
- UDP发送数据(客户端)步骤:
①创建发送端的DatagramSocket对象
②创建数据包DatagramPacket,指定数据、长度、ip地址、端口号,即指定要发送到哪
③调用DatagramSocket对象的send方法发送数据
④关闭发送端
实现如下:
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket();
String str = new String("Hello World!");
DatagramPacket dp = new DatagramPacket(str.getBytes(), str.getBytes()
.length(), InetAddress.getByName("192.168.100.162"), 8090);
ds.send(dp);
ds.close();
}
- UDP接收数据(服务端)步骤:
①创建接收端的DatagramSocket对象,并指定端口
②创建一个数据包,用于接收数据
③调用DatagramSocket的receive方法接收数据
④解析数据包,打印结果
⑤关闭接收端
实现如下:
public static void main(String[] args) throws Exception {
DatagramSocket ds = new DatagramSocket(8090);
byte[] bytes = new byte[1024];
DatagramPacket data = new DatagramPacket(bytes, bytes.length);
ds.receive(data);
byte[] newbys = data.getData();
int length = data.getLength();
//将有用的数据保留下来,长度为data.getLength()
String str = new String(newbys, 0, length);
System.out.println(str);
ds.close();
}
五、TCP编程
- TCP发送数据(客户端)步骤:
①创建客户端的Socket对象,并指定IP和端口,即指定要发送到哪
②通过Socket对象获取输出流,输出数据
③释放资源
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getByName("192.168.100.180"), 9099);
OutputStream out = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(out);
bos.write("你好,我是客户端,我发送的数据是10086".getBytes());
bos.flush();
socket.close();
}
- TCP接收数据(服务端)步骤:
①创建服务器端的ServerSocket对象,并绑定监听端口
②调用accept方法监听客户端的连接,并获取Socket对象
③通过Socket对象获取输入流,读数据
④释放资源
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(9099);
Socket socket = serverSocket.accept();
InputStream in = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
byte[] bys = new byte[1024];
int len = 0;
while((len = bis.read(bys))!=-1) {
System.out.println("客户端发送的数据是:" + new String(bys, 0, len));
}
serverSocket.close();
}
六、多线程实现TCP通信
为什么要用多线程,是因为服务器一般是一直启动的,不会关闭,因此他要一直监听客户端,所以采用多线程来实现为每一个客户端建立一个专线连接,即每有一个客户端请求,服务器就创建一个线程与之通信。
- 服务器线程,监听多个客户端
public class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\IO\\ServerFile.txt"));
String line;
while((line = br.readLine())!=null) {
System.out.println(line);
bw.write(line);
bw.newLine();
bw.flush();
}
OutputStream out = socket.getOutputStream();
out.write("我是服务器,已成功接收数据".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 服务器端
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(9099);
while(true) {
Socket socket = serverSocket.accept();
new Thread(new ServerThread(socket)).start();
}
}
}
- 客户端
public class Client {
public static void main(String[] args) throws Exception {
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9099);
BufferedReader br = new BufferedReader(new FileReader("E:\\IO\\静夜思.txt"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = "";
while((line = br.readLine())!=null) {
bw.write(line);
bw.newLine();
bw.flush();
}
socket.shutdownOutput();
InputStream in = socket.getInputStream();
byte[] bys = new byte[1024];
int len = in.read(bys);
System.out.println("我是客户端,服务器的反馈为:" + new String(bys, 0, len));
socket.close();
}
}