7.4 (java学习笔记)网络编程之TCP
2018-11-28 08:52:42来源:博客园 阅读 ()
一、TCP
1.1 TCP(Transmission Control Protocol 传输控制协议),是一种面向连接的,安全的传输协议,但效率相比于UDP而言比较低。
TCP传输时需要确保先建立连接之后,再进行传输这样就保证了传输的可靠性。
java中将TCP封装成了对应的类。
ServerSocket:服务端
Socket:客户端
1.2TCP连接的建立与取消(三次握手与四次挥手)
连接(三次握手):
1.初始状态,服务器处于监听状态,主机的传输控制模块(TCB)像服务器发送连接请求,客户端进入同步已发送状态。
2.服务器受到客服端发送的连接请求,如果同同意连接则向客户端发送确认,服务器进入同步收到状态。
3.客户端受到确认后,继续给服务器发送确认报文,客户端进入已连接状态。
后续服务器收到客服端的确认后也进入已建立连接状态。
建立连接后,客户端和服务器就可以愉快的发送信息了,信息发送完毕后,就要断开连接。
断开(四次挥手):
1.客户端发送释放报文,同时停止发送数据主动关闭TCP连接,进入终止等待状态1。
2.服务器收到释放报文后发送确认,此时服务器进入关闭等待状态。此时客户端到服务器方向的连接就释放了。
此时TCP进入半连接状态,服务器到客户端的连接未释放,此时服务器还可以将未发送完的数据向客户端发送。
3.服务器没有数据向客户端发送之后,就会发出连接释放报文等待客户端确认,服务器进入最终确认状态。
4.客户端收到服务器发送的释放报文后,向服务器发送一个确认报文,服务器进入连接关闭状态。客户端同时进入时间等待(TIME-WAIT)状态。
此时连接还没有被释放掉。客户端会等待2MSL的时间,然后进入连接关闭状态。至此连接断开完成。
每一条TCP的连接唯一的被两个通信两端的两个端点表示,也就是是四元组(源IP,源端口,目的IP,目的端口),
而不是单纯的用一个IP地址和端口区别。
也就意味着一个TCP可以建立多个连接,比如服务器IP是127.0.0.1,端口是8888;
例如客户端一:127.0.0.1:3389
客户端二:127.0.0.1:3390
客户端三:127.0.0.1:3390
三个连接对应的四元组
TCP 127.0.0.1:3889 127.0.0.1:8888 ESTABLISHED
TCP 127.0.0.1:3890 127.0.0.1:8888 ESTABLISHED
TCP 127.0.0.1:3891 127.0.0.1:8888 ESTABLISHED
我们可以发现即使目的地IP和端口相同,但本地的端口不同导致整个四元组不同。
服务器可以建立多个连接,前提是四元组不同。连接中无法出现两个四元组相同的连接。
TCP可以连接多个客户端,为其每一个客户端创建一个Socket,Socket不同代表不同连接。
客户端服务器之间通过Socket通信,服务器加上多线程为每一个Socket分配一个线程就可实现并发处理。
参考:1、计算机网络(第四版) 谢希仁编著。
2、https://www.cnblogs.com/Andya/p/7272462.html
3、https://blog.csdn.net/sssnmnmjmf/article/details/68486261
二、ServerSocket
ServerSocket(int port)//创建绑定到指定端口的服务器套字节。
默认绑定的IP地址是本地的IP地址。
例如我这里是在个人电脑上面运行,绑定的地址就是当前主机的IP地址。
当前IP地址可按win键+r输入cmd,然后输入ipconfig -all查看以太网适配器的IPv4地址,后面带有首选的。
也可以认为是绑定到127.0.0.1上,因为当前C/S都是在一台电脑上运行都属于本机访问,
所以本地测试使用的回环地址(127.0.0.1和本机IP(192.168.190.1)都可以。
2.主要方法
Socket accept()//监听要对当前对象IP上指定端口的连接,如果发现有连接请求则连接它。
例如客户端发送一个连接请求到当前服务端的对应端口,则建立客户端与服务端的连接。
这个监听是一个阻塞式的监听,意思就是说如果没有建立连接的话当前进程就不会继续向下运行。
成功建立连接后,会返回一个Socket对象,而Socket对象中有获取输入输出流的方法,这时就在
客户端,服务端之间建立输入输出流管道,两者就可以通过这个管道通信。
三、Socket
1.构造方法:
Socket(InetAddress address, int port)
Socket(String host, int port)
//创建套字节,并将其绑定到指定的(IP|域名)上的指定端口。
2.主要方法:
InputStream getInputStream()//返回当前Socket对象的输入流
OutputStream getOutputStream()//返回当前Socket对象的输出流
四、例子
Server:
import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; public class Server { public static void main(String[] args) throws UnknownHostException, IOException { String msg = "欢迎连接到Server!"; ServerSocket server = new ServerSocket(8880);//绑定到本地IP的8880端口 Socket socket = server.accept();//阻塞式接收,接收成功建立连接管道 //连接管道的输出流,即对连接对象(客户端)进行输出。 BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8")); bos.write(msg);//服务器将指定内容发给客户端 bos.newLine(); bos.flush(); } }
Client:
import java.io.BufferedReader; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) throws UnknownHostException, IOException { Socket client = new Socket("192.168.190.1",8880);//向指定IP地址的指定端口进行连接 // Socket client = new Socket("127.0.0.1",8880);//使用127.0.0.1和使用192.168.190.1都可以完成通信 //连接成功后,获取连接管道的输入流,即对服务器写入内容进行读取 BufferedReader isr = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8")); String re = isr.readLine();//读取内容 System.out.println(re); } }
运行结果:
欢迎连接到Server!
先运行Server会进行阻塞式接收,没有建立连接前后面的语句都不会执行。
然后运行Client建立连接后,Server向连接管道中写入数据,Client向连接管道中读取数据。
最后将内容显示到控制台。
五、简易聊天室
下面结合多线程,和网络编程实现一个简易聊天室。
客户端先将消息发送到服务器,服务接收消息后转发给其他客户端。
每个客户端是一个线程。
基本流程:
1.A客户端读取键盘输入数据,并将其发送到服务器。
2.服务器与A客户端建立连接后,将A客户端放入一个容器,同时将A客户端发送的消息,转发给容器中除A客户端之外的所有客户端。
服务器中为每一个Socket分配一个线程,就可以实现并发转发所有聊天消息。
3.发送给其他客户端后,其他客户端会读取服务器发送的内容并显示到自己控制台。
Send:读取键盘输入内容并将其发送给服务器
import java.io.BufferedReader; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.Socket; public class Send implements Runnable { private boolean Running = true; private DataInputStream dis;//用于读取service返回的消息 private DataOutputStream dos;//用于向server发送消息 private BufferedReader br; public Send(){ } public Send(Socket client){ try { dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); } catch (IOException e) { // TODO Auto-generated catch block System.err.println("初始化连接失败!"); Running = false; try { dis.close(); dos.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常!"); } } } //读取键盘输入信息并返回 private String reciver(){ String msg=null; br = new BufferedReader(new InputStreamReader(System.in)); try { msg = br.readLine(); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("读取用户输入异常!"); Running = false; try { br.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } return msg; } //将键盘输入信息发送至服务器 private void send(){ String msg = reciver(); try { if(msg != null && !msg.equals("")){ dos.writeUTF(msg); dos.flush(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); System.err.println("用户发送信息异常!"); Running = false; try { dos.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } } public void run(){ while(Running){ send(); } } }
reciver:读取服务器发送的数据
package ChatRoom; import java.io.DataInputStream; import java.io.IOException; import java.net.Socket; public class Reciver implements Runnable{ private boolean Running = true; private DataInputStream dis; public Reciver(){ } //初始化,获取连接 public Reciver(Socket client){ try { dis = new DataInputStream(client.getInputStream()); } catch (IOException e) { // TODO Auto-generated catch block System.err.println("Client-->Server连接失败!"); Running = false; try { dis.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常!"); } } } //读取客户端发送的数据 private String reciver(){ String msg=null; try { msg = dis.readUTF(); } catch (IOException e) { // TODO Auto-generated catch block System.err.println("接受客户端消息异常!"); Running = false; try { dis.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常!"); } } return msg; } public void run(){ while(Running){ System.out.println(reciver()); } } }
Client:(192.168.1.1~253) 255.255.255.0
import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) throws UnknownHostException, IOException { // TODO Auto-generated method stub Socket client = new Socket("192.168.1.254",8888);//连接服务器 new Thread(new Send(client)).start();//读取键盘数据并发送给服务器 new Thread(new Reciver(client)).start();//读取服务器发送回来的消息 } }
Server: 192.168.1.254 255.255.255.0
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.LinkedList; import java.util.List; public class Server { static List<Server.Connect> allUser; public static void main(String[] args) throws IOException{ allUser = new LinkedList<Server.Connect>();//存储客户端的容器 ServerSocket serverSocket = new ServerSocket(8888);//设置监听端口 while(true){//不断接受客户端的连接请求 Socket con = serverSocket.accept();//获取服务器与客户端的Socket // System.out.println(con.getPort()); Server server = new Server();//实例化一个服务器 Server.Connect connect = server.new Connect(con);//创建一个客户端到服务器的连接(socket) allUser.add(connect);//将已经连接的客户端放入容器,也可以看做将socket放入服务器 new Thread(connect).start();//每连接一个客户端(socket)就为其开辟一条线程,一个服务器对应多个客户端。 } } class Connect implements Runnable{// private boolean Running = true;//运行标志位 DataInputStream dis; DataOutputStream dos; public Connect(){ } public Connect(Socket client){//客户端连接上服务器后的socket try {//初始化获取对客户的读写流 dis = new DataInputStream(client.getInputStream()); dos = new DataOutputStream(client.getOutputStream()); } catch (IOException e) { // TODO Auto-generated catch block System.err.println("Server-->Client连接失败!"); try { dos.close(); dis.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常"); } } } public String reciver(){//读取客户端发送的消息 String msg = null; try { msg = dis.readUTF(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); System.err.println("获取客户端信息异常!"); Running = false; try { dis.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常"); } } return msg;//返回读取的消息 } public void send(String msg){//将消息发送到输出流dos对应的客户端 try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { // TODO Auto-generated catch block System.err.println("发送客户端信息异常!"); Running = false; try { dos.close(); } catch (IOException e1) { // TODO Auto-generated catch block System.err.println("关闭异常"); } } } public void sendOther(){//将消息发送到其它客户端,例如A客户端发送过来的消息,就发送给除A之外的客户端 String msg = this.reciver(); System.out.println(msg); for(Connect temp : allUser){//遍历存放客户端的容器 if(temp == this)//如果容器中当前对象时是A,就跳过这次循环,不是则将消息发送到对应的客户端。 continue; temp.send(msg);//哪一个客户端调用就将消息发给谁,假如这里的temp是B就将调用B中的send,此时发送的输出流是向客户端B写入的。 } } @Override public void run() {//开启多线程后服务器不断接收客户端消息,然后转发 // TODO Auto-generated method stub while(Running){//运行标志位,如果中途出现读写异常则终止。 sendOther(); } } } }
如果是一台电脑上测试,则将客户端中连接服务器的地址修改为127.0.0.1或localhost端口任选(大于1024即可)。
如果是多台电脑测试,例如两台电脑(将两台电脑的网线接口用一根网线连接)。
将其中一台电脑的IP地址修改为192.169.1.245:255.255.255.0,
另外一台IP地址只需和其保持同一网段即可例如(192.168.1.1:255.255.255.0)。(最好禁用其余网卡)
在192.168.1.254上先运行服务器然后运行客户端,在另外一个电脑上运行客户端。
通过控制台输入可以实现聊天。
如果想实现同一网段多个电脑间通信需要用到交换机连接多个电脑。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- 国外程序员整理的Java资源大全(全部是干货) 2020-06-12
- 2020年深圳中国平安各部门Java中级面试真题合集(附答案) 2020-06-11
- 2020年java就业前景 2020-06-11
- 04.Java基础语法 2020-06-11
- Java--反射(框架设计的灵魂)案例 2020-06-11
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash