udp服务器和tcp服务器.
udp服务器按照数据报的形式发送,数据包即字节数组,然后经过五层协议在网络上进行传输.
tcp服务器面向字节流,也即字符串,规定收发双发的数据必须相同,所以一般是按行读取和发送.
tcp服务器执行过程:
1.首先建立连接,通过实例化一个Socket对象(即网卡)实现.其中内核会自动通过三次握手建立连接,建立连接是保证客户端和服务器端的收发能力正常.
2.建立连接后内核将Socket放入阻塞队列,然后服务器端通过accept方法取出连接,对连接进行处理,相当于打电话,一个人和另外几个人拨通电话,然后按照这几个人的先后顺序一个一个进行通话交流,但是这种方式只能一次处理一个连接,所以用多线程并发机制可以实现多个连接并发处理.
3.处理过程包括长连接和短连接,长连接是指一个连接中会有很多次交互,一般通过循环实现,短连接是指只能有一次交互.连接终止的时候客户端会先终止,然后服务器端才会终止,服务器为客户端服务.
当客户端和服务器端在收发过程中时,如果一方没有发送数据,则另一方会阻塞,知道另一方发送数据为止.
注意:在发送后,要进行flush操作,这是因为数据发送之前放到了内存中的缓冲区里,而最终需要经过数据链路层的网卡进行发送,flush操作会将缓冲区的数据放到网卡中,由网卡给数据加上mac地址,封装成帧,然后再由物理层转换为光电信号在网络介质中进行传输.如果不进行flush操作,则数据就会一直停留在缓冲区里,不会放到网卡中.另外,udp是自动随机将寻找mac路径,但是tcp已经建立连接,不用再寻找路径,直接用之前的就可以.
用tcp服务器实现简单的回显服务器.
**tcp:**客户端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
/**
* @author :liuheizi
* @date :Created in 2022/2/5 15:09
* @description:TCP代码
* @modified By:
* @version: 1.8.0_192
*/
class EchoSever {
private ServerSocket serverSocket=null;
public EchoSever(int port) throws IOException {
this.serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while (true) {
Socket socket = serverSocket.accept();
poccess(socket);
}
}
public void poccess(Socket n) {
System.out.println("客户端端口号:" + n.getInetAddress().toString());
System.out.println("客户端端口号:" + n.getPort());
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(n.getInputStream()));
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(n.getOutputStream()))
) {
while (true) {
String request = bufferedReader.readLine();
String response = poccess1(request);
bufferedWriter.write(response+"\n");
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端退出");
}
}
public String poccess1(String request) {
return request;
}
}
public class Mytest {
public static void main(String[] args) throws IOException {
EchoSever myServer = new EchoSever(9090);
myServer.start();
}
}
tcp客户端:
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* @author :liuheizi
* @date :Created in 2022/2/5 15:56
* @description:
* @modified By:
* @version: 1.8.0_192
*/
class EchoClient1 {
private Socket socket;
public EchoClient1(String serverIp, int serverPort) throws IOException {
this.socket = new Socket(serverIp, serverPort);
}
public void start() {
Scanner scanner = new Scanner(System.in);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()))
) {
while (true) {
System.out.println("请输入");
String request = scanner.nextLine();
if ("exit".equals(request)) {
break;
}
bufferedWriter.write(request+"\n");
bufferedWriter.flush();
String response=bufferedReader.readLine();
System.out.println(response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class MyClientTest {
public static void main(String[] args) throws IOException {
EchoClient1 echoClient = new EchoClient1("127.0.0.1",9090);
echoClient.start();
}
}
缓冲区的几种情况:
1.tcp服务器
2.文件传输(io流)
3.从控制台读取和打印
并发处理的服务器端代码:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.Buffer;
/**
* @author :liuheizi
* @date :Created in 2022/2/5 15:09
* @description:TCP代码
* @modified By:
* @version: 1.8.0_192
*/
class EchoSever {
private ServerSocket serverSocket=null;
public EchoSever(int port) throws IOException {
this.serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
**while (true) {
Socket socket = serverSocket.accept();
Thread t1=new Thread(){
@Override
public void run() {
poccess(socket);
}
};
t1.start();
}**
}
public void poccess(Socket n) {
System.out.println("客户端端口号:" + n.getInetAddress().toString());
System.out.println("客户端端口号:" + n.getPort());
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(n.getInputStream()));
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(n.getOutputStream()))
) {
while (true) {
String request = bufferedReader.readLine();
String response = poccess1(request);
bufferedWriter.write(response+"\n");
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("客户端退出");
}
}
public String poccess1(String request) {
return request;
}
}
public class Mytest {
public static void main(String[] args) throws IOException {
EchoSever myServer = new EchoSever(9090);
myServer.start();
}
}
实现服务器的关键是通过请求计算响应,也就是上图中的pocces1函数.
udp实现回显服务器
服务器端:
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
/**
* @author :liuheizi
* @date :Created in 2022/2/5 15:56
* @description:
* @modified By:
* @version: 1.8.0_192
*/
public class UdpEchoSever {
private DatagramSocket datagramSocket;
public UdpEchoSever(int port) throws SocketException {
this.datagramSocket = new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while (true) {
DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
datagramSocket.receive(requestPacket);
String request = new String(requestPacket.getData(),0,requestPacket.getLength()).trim();
String response = poccess(request);
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,
requestPacket.getSocketAddress());
datagramSocket.send(responsePacket);
System.out.println("客户端ip:" + requestPacket.getAddress());
System.out.println("客户端端口号:"+requestPacket.getPort());
}
}
public String poccess(String request){
return request;
}
public static void main(String[] args) throws IOException {
UdpEchoSever udpEchoSever = new UdpEchoSever(9090);
udpEchoSever.start();
}
}
客户端:
import javax.xml.crypto.Data;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
/**
* @author :liuheizi
* @date :Created in 2022/2/5 19:48
* @description:
* @modified By:
* @version: 1.8.0_192
*/
public class UdpEchoClient {
private DatagramSocket datagramSocket;
private String severIp;
private int severPort;
public UdpEchoClient(String severIp, int severPort) throws SocketException {
this.datagramSocket = new DatagramSocket();
this.severIp=severIp;
this.severPort = severPort;
}
public void start() throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("服务器启动");
while (true) {
System.out.println("请输入");
String request=scanner.nextLine();
if ("exit".equals(request)) {
break;
}
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,
InetAddress.getByName(severIp),severPort);
this.datagramSocket.send(requestPacket);
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
datagramSocket.receive(responsePacket);
String response = new String(requestPacket.getData(),0,requestPacket.getLength()).trim();
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);
udpEchoClient.start();
}
}
比较:
udp效率更高,因为不用建立连接,但是数据的安全性不高,容易丢失;tcp数据安全性高,但是效率低,因为需要需要频繁的建立连接和销毁连接.
udp代码比较繁琐,但是原理简单,即字节数组传输,tcp代码简单,但原理比较复杂,需要建立连接.
udp可以多个客户端访问,表现为哪个客户端给服务器发送数据了,服务器就接收数据并处理返回,此处是没有并发的,而不是向tcp那样一个一个客户端进行处理,tcp解决这个问题是通过并发线程.
多个客户端访问同一个服务器怎样简单实现?
通过xshell窗口,Java 包.类命令:
快捷键:shift加右键