1.TCP通信
1.1 Socket原理
Socket简介 Socket通常称作套接字用于描述IP地址和端口,是一个通信连接的句柄。 在Internet上的主机一般运行了多个服务软件,同时提供集中服务。 每种服务都打开一个Soket,并绑定一个端口上,不同的端口对应于不同的服务。 应用程序通常通过“套接字”向网络发出请求或者应答网络请求。Socket和ServerSocket类位于java.net包中。ServerSocket用于服务端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会生成一个Socket实例,操作这个实例,完成所需要的会话。
1.2 获取本地地址和端口号
Java.net.Socket为套接字类,其提供了很多方法, 其中我们可以通过Socket获取本地的地址以及端口号。 Int getLocalPort(); 该方法用于获取本地使用的端口号 InetAddress getLocalAddresss(); 该方法用于获取套接字绑定的本地地址 使用InetAddress获取本地的地址方法: String getCanonicalHostName(); 获取此IP地址的完全限定域名。 String getHostAddress() 返回IP地址字符串(以文本表现形式)
public static void testSocket() throws Exception {
Socket socket = new Socket("127.0.0.1",8088);
InetAddress add = socket.getLocalAddress();
System.out.println(add.getCanonicalHostName());
System.out.println(add.getHostAddress());
System.out.println(socket.getLocalPort());
}
1.3 获取远端地址和端口号
通过Socket获取远端的地址以及端口号。 Int getPort(); 该方法用于获取远端使用的端口号 InetAddress.getInetAddress(); 该方法用于获取套接字绑定的远端地址
public static void testSocket() throws Exception {
Socket socket = new Socket("localhost",8088);
InetAddress inetAdd = socket.getLocalAddress();
System.out.println(inetAdd.getCanonicalHostName());
System.out.println(inetAdd.getHostAddress());
System.out.println(socket.getPort());
}
1.4.获取网络输入流和网络输出流
通过Socket获取输入流于输出流,这两个方法是使用Socket通讯的关键方法。
inputStream getInputStream() 该方法用于返回此套接字的输入流。 OutputStream.getOutputStream() 该方法用于返回此套接字的输出流。
public static void testSocket() throws Exception {
Socket socket = new Socket("localhost",8088);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
}
1.5 close方法
当使用Socket进行通讯完毕后,要关闭Socket已释放系统资源。 Void close(); 关闭此套接字。当关闭了该套接字后也会同时关闭由此获取的输入流与输出流。
1.6 Server端ServerScoket监听
Java.net.ServerSocket是运行于服务端应用程序中。通常创建ServerSocket需要指定服务端口,之后监听Socket的连接:
ServerSocket server = new ServerSocket(8088);
Socket socket = server.accept();
1.7 Client端Socket连接
当服务端创建ServerSocket并通过accept()方法侦听后,我们就可以通过客户端应用程序中创建Socket来向服务端发起连接。 需要注意的是,创建Socket的同时就发起连接,若连接异常会抛出异常。
ServerSocket server = new ServerSocket(8088);
1.8 C-S端通信模型
1.9. 聊天室案例
使用Socket于ServerSocket建立客户端于服务端的连接并使用客户端向服务端发送一条消息
改善代码,使用户可以重复通过在控制台输入内容,并将内容发送给服务端。
Server.java
package day05.ZHONG;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
private static BufferedReader br=null;
private static PrintWriter pw=null;
private static ServerSocket ss;
private static Socket s;
static Scanner scanner=new Scanner(System.in);
public static void main(String[] args) {
try {
ss=new ServerSocket(5500);
System.out.println("服务器正常启动。。。。");
s=ss.accept();
System.out.println("连接成功"+s.getRemoteSocketAddress());
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
while(true){
String string=br.readLine();
System.out.println("Server读到:"+string);
System.out.println("Server端请输入:");
String str=scanner.next();
pw.println(str);
pw.flush();
if(str.equals("exit")){
break;
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
try {
pw.close();
br.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
Client
package day05.ZHONG;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
private static PrintWriter pw=null;
private static BufferedReader br=null;
private static Socket s;
static Scanner scanner=new Scanner(System.in);
public static void main(String[] args) {
try {
Socket s=new Socket(InetAddress.getLocalHost(),5500);
pw=new PrintWriter(new OutputStreamWriter(s.getOutputStream()));
br=new BufferedReader(new InputStreamReader(s.getInputStream()));
while(true){
System.out.println("Client端请输入:");
String str = scanner.next();
pw.println(str);
pw.flush();
String string=br.readLine();
System.out.println("Client读到:"+string);
if(str.equals("exit")){
break;
}
}
}catch (Exception e) {
e.printStackTrace();
}
try {
br.close();
pw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
1.10 Server端多线程模型
如果向是一个服务端可以支持多客户端连接,我们需要了解以下问题: 循环调用accept方法侦听客户端的连接 使用线程来处理单一客户端的数据交互
因为需要处理多客户端,所以服务端要周期性循环调用accept方法,但该方法会产生阻塞,所以与某个客户端的交互就需要使用线程来并发处理。
重构聊天室案例,使用线程来实现一个客户端可以同时接受多个客户端的信息。
1.10.1 Server.java
package day05.ZHONG.duo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Server {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket ss = new ServerSocket(10086);
System.out.println("服务器正常启动。。。");
while(true){
Socket socket = ss.accept();
System.out.println("用户接入成功。。。");
new ServerThread(socket).start();
}
}
}
1.10.2 Client.java
package day05.ZHONG.duo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException, InterruptedException {
Socket socket = new Socket("127.0.0.1",10086);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Scanner scanner = new Scanner(System.in);
while(true){
String arg = scanner.nextLine();
if("exit".equals(arg.trim())){
pw.println("exit");
pw.flush();
break;
}
pw.println(arg);
pw.flush();
String str = br.readLine();
System.out.println("Client read = "+str);
}
pw.close();
br.close();
}
}
1.10.3 ServerThread.java
package day05.ZHONG.duo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;
public class ServerThread extends Thread {
private Socket socket;
private InetAddress ip;
public ServerThread(Socket socket) {
ip =socket.getInetAddress();
this.socket = socket;
}
@Override
public void run() {
BufferedReader br = null;
PrintWriter pw = null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String string = br.readLine();
System.out.println("Server read = 服务端"+ip+"说:" + string);
System.out.println("Server端请输入: ");
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
pw.println( "【"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())
+ "】服务端说:"+str);
pw.flush();
if ("exit".equals(str)) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally{
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
pw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
1.11聊天室群发
重构聊天室案例,使服务器可以将用户的信息转发给所有客户端。并在每个客户端控制台上显示。
Server.java
package day05;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class Server {
private ServerSocket server;
private List<PrintWriter> allOut;
public Server(){
try {
allOut = new ArrayList<PrintWriter>();
server = new ServerSocket(8088);
} catch (Exception e) {
e.printStackTrace();
}
}
private synchronized void addOut(PrintWriter out){
allOut.add(out);
}
private synchronized void removeOut(PrintWriter out){
allOut.remove(out);
}
private synchronized void sendMessageToAllClient(String message){
for(PrintWriter out : allOut){
out.println(message);
}
}
public void start(){
try {
System.out.println("等待客户端链接...");
Socket socket = server.accept();
System.out.println("一个客户端链接了!");
ClientHandler handler
= new ClientHandler(socket);
Thread t = new Thread(handler);
t.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
private class ClientHandler implements Runnable{
private Socket socket;
private String host;
public ClientHandler(Socket socket){
this.socket = socket;
InetAddress address
= socket.getInetAddress();
host = address.getHostAddress();
System.out.println("["+host+"]上线了!");
}
public void run(){
PrintWriter pw = null;
try {
OutputStream out
= socket.getOutputStream();
OutputStreamWriter osw
= new OutputStreamWriter(out,"UTF-8");
pw = new PrintWriter(osw,true);
addOut(pw);
InputStream in = socket.getInputStream();
InputStreamReader isr
= new InputStreamReader(in,"UTF-8");
BufferedReader br
= new BufferedReader(isr);
String message = null;
while((message = br.readLine())!=null){
System.out.println("["+host+"]说:"+message);
sendMessageToAllClient(
"["+host+"]说:"+message);
}
} catch (Exception e) {
} finally{
removeOut(pw);
try {
socket.close();
System.out.println("["+host+"]下线了!");
} catch (Exception e2) {
}
}
}
}
}
Client.java
package day05;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class Client {
private Socket socket;
public Client(){
try {
socket = new Socket("localhost",8088);
} catch (Exception e) {
e.printStackTrace();
}
}
public void start(){
try {
GetServerMessageHandler handler = new GetServerMessageHandler();
Thread t = new Thread(handler);
t.start();
OutputStream out = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
PrintWriter pw= new PrintWriter(osw,true);
Scanner scanner = new Scanner(System.in);
while(true){
pw.println(scanner.nextLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Client client = new Client();
client.start();
}
private class GetServerMessageHandler
implements Runnable{
public void run(){
try {
InputStream in= socket.getInputStream();
InputStreamReader isr = new InputStreamReader(in,"UTF-8");
BufferedReader br = new BufferedReader(isr);
String message = null;
while((message = br.readLine())!=null){
System.out.println(message);
}
} catch (Exception e) {
}
}
}
}
2.UDP通信
2.1构建接收包
DatagramPacket:UDP数据包基于IP建立的,每台主机有65535个端口可以使用。 构建接收包 DatatgramPacket(byte[] buf,int length) 将数据包中length长度的数据装进buf数组。
DatagramPacket(byte[] buf, int offset, int length) 将数据包中从offset开始、length长度的数据装进buf数组。
2.2 构建接收包
构造发送包 DatagramPacket(byte[] buf , int length , InetAddress clientAdress,int clientPort) 从buf数组中取出length长度的数据创建数据包对象,目标是clientAddress地址,clientPort端口,通常用来发送数据给客户端。
|