1.Socket
基于TCP的面向连接的 安全可靠但是效率就会变低;
不同的协议的端口号是可以重复的,同一个协议不可以;
Tcp客户端的端口是电脑自己指定的 不需要我们分配;
Tcp的1024一下的端口号不要使用是留给系统的;
Socket连接的示意图:
接收客户端连接 阻塞式
Socket socket =ser.accept(); 所谓阻塞式就是不接收到不能继续执行程序
一个服务器一个客户的示例:
package TCP;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
必须先启动服务器 后连接
1、创建服务器 指定端口 ServerSocket(int port)
2、接收客户端连接
3、发送数据+接收数据
*
*/
public class Server {
public static void main(String[] margs) throws IOException {
//1、创建服务器 指定端口 ServerSocket(int port)
ServerSocket ser = new ServerSocket(8888);
//2、接收客户端连接 阻塞式
Socket socket =ser.accept(); //特别要注意这个地方就是服务器端要接收 客户端的链接 而且他在调用getOutputStream()时要用socket.getOutputStream()而不是用ser.
System.out.println("一个客户端建立连接");
//3、返回数据
String msg ="欢迎使用";//我们在这里发送一个字符串
/*
//这里我们用输出流来发送数据 因为我们发送的是String 为了更方便的处理他 我们使用bufferedWriter包装一下
//我们用buffered的主要目的是为了有 写一行行分隔符。 来结束那个阻塞式的方法
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//
bw.write(msg);//这个方法也是阻塞式的 所以他一定要加上 换行符
bw.newLine();//newLine()
// 写一行行分隔符。
bw.flush(); //注意这个流不要关闭 因为这样就会把用户与服务器之间的链接给关掉;
//这里我们处理字符串的方法有点复杂 我们可以直接使用处理数据加类型的DataOutputStream()
//这里我们在改变流的时候要对应改变
*/
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(msg);
dos.flush();
}
}
package TCP;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
/**
*
* 1、创建客户端 必须指定服务器+端口 此时就在连接
* Socket(String host, int port)
* 2、接收数据 +发送数据
* @author Wang
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
//1、创建客户端 必须指定服务器+端口 此时就在连接
Socket client = new Socket("localhost",8888);
//2、接收数据 +发送数据
/*BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));
String accept = br.readLine();
System.out.println(accept);*/
DataInputStream dis = new DataInputStream(client.getInputStream());
String accept = dis.readUTF();
System.out.println(accept);
}
}
注意: 我们来说一下我们服务器与客户端交互的过程:他们是先建立一个链接,然后客户端发给服务器的发是客户端用Output发给内存,然后服务端从内存中读取;
package TCPchat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
*
* 我们先来写一个简单的聊天室的交互
* @author Wang
*
*/
public class Server {
public static void main(String[] args) throws IOException {
/*//1.先创建一个服务器 并指定一个端口号
ServerSocket server = new ServerSocket(6666);
//2.与客户端建立连接
Socket socket = server.accept();
//3.接收客户端发回来的信息
DataInputStream dis = new DataInputStream(socket.getInputStream());
String accept = dis.readUTF();
System.out.println(accept);
//4.我们吧接收到的数据再返还给客户端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
dos.flush();*/
ServerSocket server = new ServerSocket(6666);
Socket socket = server.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
//2.与客户端建立连接
while(true) {
//3.接收客户端发回来的信息
String accept = dis.readUTF();
//System.out.println(accept);
//4.我们吧接收到的数据再返还给客户端
dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
dos.flush();
}
}
}
package TCPchat;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
/*//1.创建一个客户端 并于服务器建立连接
Socket client = new Socket("localhost",6666);
//2.准备一个数据
String sent = "Cilent send to Server";
//3.使用流将这个数据发送给服务器;
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF(sent);
dos.flush();
//4.我们接收服务器发送的数据
DataInputStream dis = new DataInputStream(client.getInputStream());
String accept = dis.readUTF();
System.out.println(accept);*/
/*//1.创建一个客户端 并于服务器建立连接
Socket client = new Socket("localhost",6666);
//2.准备一个数据
//我们来发送从控制台输入的数据
BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); //我们从这里可以看出System.in 属于字节的输入流
//因为我们在这里用的转换流
String input = console.readLine();//这里肯定是一个阻塞式的方法
//3.使用流将这个数据发送给服务器;
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
dos.writeUTF(input);
dos.flush();
//4.我们接收服务器发送的数据
DataInputStream dis = new DataInputStream(client.getInputStream());
String accept = dis.readUTF();
System.out.println(accept);
//我们在这里会发现 我们的程序只能运行一次 只是接收到客户发的一次信息就结束了
我们来改进一下
就是加一个while的循环呗*/
//1.创建一个客户端 并于服务器建立连接
Socket client = new Socket("localhost",6666);
//2.准备一个数据
//我们来发送从控制台输入的数据
BufferedReader console = new BufferedReader(new InputStreamReader(System.in)); //我们从这里可以看出System.in 属于字节的输入流
//因为我们在这里用的转换流
//3.使用流将这个数据发送给服务器;
DataOutputStream dos = new DataOutputStream(client.getOutputStream());
DataInputStream dis = new DataInputStream(client.getInputStream());
while(true) {
String input = console.readLine();//这里肯定是一个阻塞式的方法
dos.writeUTF(input);
dos.flush();
//4.我们接收服务器发送的数据
String accept = dis.readUTF();
System.out.println(accept);
//注意我们的循环发送 服务器那边也要对应起来
}
}
}
注意:我们上面的代码只能实现客户端的先发送在接收,而服务器反之(这里是因为代码是按照顺序从上往下执行的);
这与我们实际的聊天情况不太一样 我们实际聊天是 你可以随意放松 根本不用接收完才能发送,那么这里就会让我们联想到多线程的问题,那么下面我们用多线程简单的模拟一下;
server还是上面的那个server
package TCPchatDemo02;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
/*//1.先创建一个服务器 并指定一个端口号
ServerSocket server = new ServerSocket(6666);
//2.与客户端建立连接
Socket socket = server.accept();
//3.接收客户端发回来的信息
DataInputStream dis = new DataInputStream(socket.getInputStream());
String accept = dis.readUTF();
System.out.println(accept);
//4.我们吧接收到的数据再返还给客户端
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
dos.flush();*/
ServerSocket server = new ServerSocket(9999);
Socket socket = server.accept();
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
//2.与客户端建立连接
while(true) {
//3.接收客户端发回来的信息
String accept = dis.readUTF();
//System.out.println(accept);
//4.我们吧接收到的数据再返还给客户端
dos.writeUTF("我是服务器收到后然后返还给你的: "+accept);
dos.flush();
}
}
}
package TCPchatDemo02;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
*
* 消息的发送
*
* @author Wang
*
*/
public class Send implements Runnable{
//控制台输入流
private BufferedReader consoleInput;
//消息的发送
private DataOutputStream dos;
//设置一个消息发送的标志位
boolean flag = true;
public Send() {//无参构造先把控制台的输入传进来
consoleInput = new BufferedReader(new InputStreamReader(System.in));
}
public Send(Socket socket) {
this();
try {
dos = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
//如果这里建立不了通道 我们关闭流和停止线程的运行
flag =false;
CloseUtil.closeAll(dos,consoleInput);
}
}
/**
* 1.我们从控制台读取数据 如果读取不到就会返回一个"";(空串)
*/
public String getConsoleInput() {
try {
return consoleInput.readLine();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
/**
* 2.我们把从控制台读取的数据发送给服务器
*/
public void send() {
String message = getConsoleInput();
if((message != null) && (message != "") ) {
try {
dos.writeUTF(message);
dos.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
flag =false;
CloseUtil.closeAll(dos,consoleInput);
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
while(flag) {
send(); //线程体里面的内容就是一直发送
}
}
}
package TCPchatDemo02;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
*
* 接收数据 与发送数据的思路相同
*
* @author Wang
*
*/
public class Receive implements Runnable{
private DataInputStream dis;
private boolean flag = true;
Receive() {
}
Receive(Socket socket) {
try {
dis = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
flag =false;
CloseUtil.closeAll(dis);
}
}
/**
* 我们来获取读取的信息
*/
public String getReaciveData() {
try {
return dis.readUTF();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
flag =false;
CloseUtil.closeAll(dis);
}
return null;
}
/**
* 直接用线程体 输出读取的数据
*/
@Override
public void run() {
//线程体
while(flag){
System.out.println(getReaciveData());
}
}
}
package TCPchatDemo02;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
/**
*
* 我们来用多线程来实现 聊天的发送消息和接收消息
* 我们把他们包装成send 和 receive
*
* @author Wang
*
*/
public class Client {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket client = new Socket("localhost",9999);//创建一个客户端并于服务器建立连接
new Thread(new Send(client)).start(); //建立一个发送消息的线程
new Thread(new Receive(client)).start(); //建立一个接受消息的线程
}
}