1.概念
什么是⽹络编程?
⽹络编程,指⽹络上的主机,通过不同的进程,以编程的⽅式实现⽹络通信(或称为⽹络数据传
输)。
发送端和接收端
在⼀次⽹络数据传输时:
发送端:数据的发送⽅进程,称为发送端。发送端主机即⽹络通信中的源主机。
接收端:数据的接收⽅进程,称为接收端。接收端主机即⽹络通信中的⽬的主机。
收发端:发送端和接收端两端,也简称为收发端。
请求和响应
⼀般来说,获取⼀个⽹络资源,涉及到两次⽹络数据传输:
• 第⼀次:请求数据的发送
• 第⼆次:响应数据的发送。
网络传输数据的基本单位;
1.数据报 Datagram UDP
2.数据段 Segment TCP
3.数据包 Packet IP
4.数据帧 Frame 数据链路层
客⼾端和服务端
服务端:在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。
客⼾端:获取服务的⼀⽅进程,称为客⼾端。
常⻅的客⼾端服务端模型
最常⻅的场景,客⼾端是指给⽤⼾使⽤的程序,服务端是提供⽤⼾服务的程序:
- 客⼾端先发送请求到服务端
- 服务端根据请求数据,执⾏相应的业务处理
- 服务端返回响应:发送业务处理结果
- 客⼾端根据响应数据,展⽰处理结果(展⽰获取的资源,或提⽰保存资源的处理结果)
2.Socket套接字
概念
Socket套接字,是由系统提供⽤于⽹络通信的技术,是基于TCP/IP协议的⽹络通信的基本操作单元。基于Socket套接字的⽹络程序开发就是⽹络编程。
操作系统提供的socket API:
1.流式套接字----给TCP使用的
2.数据报套接字—给UDP使用的
3.Unix域套接字–不能跨主机通信,本地机进程与进程之间的通信方式
TCP和UDP都是传输层协议,都是给应用层提供服务的
TCP和UDP的特点:
TCP:有连接(打电话,通信双方保存对方的信息),可靠传输(可以尽量让数据完整发送给对方,小概率可能会丢包,保证了可靠效率就降低了),面向字节流(如文件操作就是字节流等等。。。),全双工(双向通信)
UDP:无连接(发短信,通信双方不保存对方的信息),不可靠传输(有概率丢包,这个效率高),面向数据报(一次读写只能读写一个完整的数据报(datagram)),全双工
3.UDP
UDP的socket api的两个类
1.DatagramSocket
DatagramSocket就是对于操作系统的Socket概念的封装(系统中的socket,可以理解为一种文件,socket文件就可以视为“网卡”这种硬件设备的抽象表现形式,针对socket文件进行读写操作就相当于针对网卡这个硬件设备进行读写)
总结:此处的DatagramSocket就是操作网卡的“遥控器”。
方法:
void receive(DatagramPacket p)
void send(DatagramPacket p)
void close() (socket也是一种文件,用完了要关闭,否则会占用文件描述符表的一个表项)
2.DatagramPacket
针对UDP数据报的抽象表示,一次发送/接受,就是以数据报为单位
整体流程是:
服务端
public class udpEchoserver {
DatagramSocket datagramSocket=null;
public udpEchoserver(int port) throws SocketException {
datagramSocket=new DatagramSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while (true){
//1.接受客户端的请求
DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
datagramSocket.receive(requestPacket);
String request=new String(requestPacket.getData(),0,requestPacket.getLength());
//2.计算响应
String response=process(request);
//返回响应
DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestPacket.getSocketAddress());
datagramSocket.send(responsePacket);
System.out.printf("[%s:%d] req=%s,resp=%s",requestPacket.getAddress(),requestPacket.getPort(),request,response);
}
}
private String process(String request) {
return request;
}
public static void main(String[] args) throws IOException {
udpEchoserver udpEchoserver=new udpEchoserver(8156);
udpEchoserver.start();
}
}
客户端
public class udpEchoClient {
DatagramSocket socket=null;
String severIp;
int port;
public udpEchoClient(String severIp, int port) throws SocketException {
socket=new DatagramSocket();
this.severIp =severIp;
this.port = port;
}
public void start() throws IOException {
Scanner scanner=new Scanner(System.in);
while (true){
System.out.println("请输入发送的消息:");
String request =scanner.next();
DatagramPacket requestPacket=new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(severIp),port);
//给指定IP和端口号发送信息,InetAddress.getByName()方法是把点分十进制的转化成java能识别的32位二进制字符串(点分十进制是给人看的)
socket.send(requestPacket);
DatagramPacket severPacket=new DatagramPacket(new byte[4096],4096);
socket.receive(severPacket);
//客户端发送完后,这里会有短暂的阻塞,(把请求发出去响应回来时ms级,但是顺序执行是ns级)如果不阻塞这里执行啥东西都没。并且这里的阻塞功能api已经实现好了
String response=new String(severPacket.getData(),0,severPacket.getLength());
System.out.println(response);
}
}
public static void main(String[] args) throws IOException {
udpEchoClient udpEchoClient=new udpEchoClient("127.0.0.1",8156);
udpEchoClient.start();
}
}
实现本机间的交互
如何启动多个客户端?
实现多个客户端对服务器进行交互
注意,上述如果将ip改为你女朋友的IP除非连着同一个wife(局域网内),将代码客户端代码给女朋友能实现相互交互,否则不能进行非局域网的传输,那么如何实现任何人都能连上我们的服务器呢?这就需要一个公网IP,租一个云服务器
在所有IP地址中:
这三种情况是私网IP,剩下的都是公网IP
1)10.*
2)172.16-172.31.*
3)192.168.*
TCP套接字
API 介绍
ServerSocket
ServerSocket 是创建TCP服务端Socket的API。
ServerSocket 构造⽅法:
服务端
public class TcpEchoSever {
ServerSocket serverSocket=null;
public TcpEchoSever(int port) throws IOException {
serverSocket = new ServerSocket(port);
}
public void start() throws IOException {
System.out.println("服务器启动");
while(true){
Socket clientSocket=serverSocket.accept();
//与客户端建立联系
processConection(clientSocket);
}
}
private void processConection(Socket clientSocket) throws IOException {
System.out.printf("[%s,%d]客户端上线",clientSocket.getInetAddress(),clientSocket.getPort());
try(InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream()) {
Scanner scanner=new Scanner(inputStream);
while(true){
if(!scanner.hasNext()){
System.out.printf("[%s:%d]读取完毕,客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());
break;
}
String request=scanner.next();
String response=process(request);
outputStream.write(response.getBytes(),0,response.getBytes().length);
System.out.printf("[%s:%d] req=%s resp=%s",serverSocket.getInetAddress(),serverSocket.getLocalPort(),request,response);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
clientSocket.close();
}
}
private String process(String request) {
return request+"\n";
}
public static void main(String[] args) throws IOException {
TcpEchoSever tcpEchoSever=new TcpEchoSever(9090);
tcpEchoSever.start();
}
}
服务端
public class TcpEchoClient {
Socket socket=null;
public TcpEchoClient(String severIp,int serverPort) throws IOException {
socket=new Socket(severIp,serverPort);
}
public void start() throws IOException {
System.out.println("客户端启动");
Scanner scannerConsole=new Scanner(System.in);
try(InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream()){
Scanner scannerNetWork=new Scanner(inputStream);
while (true){
System.out.println("请输入要发送的数据:");
String request= scannerConsole.next();
request+="\n";
outputStream.write(request.getBytes());
if(!scannerConsole.hasNext()){
break;
}
String response=scannerNetWork.next();
System.out.println(response);
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);
tcpEchoClient.start();
}
}