一、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();//绑定到本地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",);//向指定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上先运行服务器然后运行客户端,在另外一个电脑上运行客户端。

通过控制台输入可以实现聊天。

如果想实现同一网段多个电脑间通信需要用到交换机连接多个电脑。

7.4 (java学习笔记)网络编程之TCP的更多相关文章

  1. 7.3(java学习笔记)网络编程之UDP

    一.UDP UDP的全称是User Datagram Protocol(用户数据报协议),是一种无连接的不安全的传输协议, 传输数据时发送方和接收方无需建立连接,所以是不安全的. 发送时不建立连接直接 ...

  2. java学习之网络编程之echo程序

    服务端的实现 package com.gh.echo; import java.io.*; import java.net.*; /** * echo服务器程序 * 实现 不断接收字符串 ,然后返回一 ...

  3. java 26 - 9 网络编程之 TCP协议多用户上传文件

    TCP实现多用户上传文件: 需要同时给多用户上传文件,这样就得用多线程来实现. 实际上,这样的话,上传的先后顺序和速度就跟客户端的带宽有关:带宽够,就容易抢占到线程的执行权: 首先,创建个线程类:(这 ...

  4. java 26 - 8 网络编程之 TCP协议上传图片

    上次的是上传TXT文件,这次上传的是图片.同样,上传成功需要反馈给客户端. 区别: TXT文件用记事本打开,我们可以看得懂,所以用了缓冲字符流,对通道内的字节流进行包装了. 而图片用记事本打开,我们看 ...

  5. java 26 - 8 网络编程之 TCP协议的练习

    TCP练习: 1.客户端键盘录入,服务器输出文本文件 客户端代码: public class ClientDemo { public static void main(String[] args) t ...

  6. java 26 - 6 网络编程之 TCP协议 传输思路 以及 代码

    TCP传输 Socket和ServerSocket 建立客户端和服务器 建立连接后,通过Socket中的IO流进行数据的传输 关闭socket 同样,客户端与服务器是两个独立的应用程序 TCP协议发送 ...

  7. java 26 - 7 网络编程之 TCP协议代码优化

    上次所写的代码中,客户端和服务器端所进行的数据传输所用的是字节流. 优化: A:这次,为了高效,对这个字节流通过转换流来进行包装,包装成高效字符流. B:这次,传输的数据是通过键盘录入的数据. 服务器 ...

  8. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, /* *TCP *建立连接,形成传输数据的通道: *在连接中进行大数据量传输: *通过三次握手 ...

  9. Java网络编程之TCP、UDP

    Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载.   ...

  10. Java网络编程之TCP

    Java网络编程之TCP ​ TCP主要需要两个类:Socket和ServerSocket,Socket是客户端连接服务器时创建,参数需要指定服务器的ip和端口,ServerSocket是服务器端创建 ...

随机推荐

  1. Dream------Java--ant zip 对压缩文件进行指定位置的修改

    ant zip 对压缩文件进行指定位置的修改 实现功能: 对2中文件进行修改: 需求: 在XX文件中,从二进制流的200字节位置开始,往后的30位字节数量.插入一个值 由于涉及到公司内部,不方便写太多 ...

  2. Java中关于变量的几种情况

    Java中关于变量的几种情况 1.继承时变量的引用关系 class Animals { int age = 10; void enjoy() { System.out.println("An ...

  3. reshape中的-1

    >>> a = np.array([[1,2,3], [4,5,6]]) >>> np.reshape(a, (3,-1)) # the unspecified v ...

  4. Ubuntu_安装Wiz笔记

    前言 安装完成了Linux,有了搜狗输入法,我们还需要笔记软件,本文主要介绍如何安装为知笔记 安装步骤 找到wiz官网:http://www.wiz.cn/ 获取Linux安装教程 安装QT 下载的Q ...

  5. 十五、springboot集成定时任务(Scheduling Tasks)(二)之(线程配置)

    配置类: /** * 定时任务线程配置 * */ @Configuration public class SchedulerConfig implements SchedulingConfigurer ...

  6. 查找Python包的依赖包(语句)

    Window 10家庭中文版,Python 3.6.4, 今天看完了urllib3的官文(官方文档),因为没有具体使用过,所以,仍然是一知半解,但是,突然想知道 urllib3以及前面学习过的requ ...

  7. 密码记录工具keepass保存密码

    https://www.cnblogs.com/wicub/p/5753005.html

  8. No.14 selenium for python table表单

    table表单,HTML中的特征 标识性标签:table.tr.th.td 定位使用Xpath定位 点击表格中的元素即可

  9. 微信 JS API 支付教程

    最近一个项目中用到了微信开发,之前没有做过支付相关的东西,算是拿这个来练练手,刚开始接触支付时候很懵逼,加上微信支付开发文档本来就讲得不清楚,我是彻底蒙圈了,参考了很多代码之后,算是有一点思路了. 用 ...

  10. ZOJ 3781 Paint the Grid Reloaded(DFS连通块缩点+BFS求最短路)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5268 题目大意:字符一样并且相邻的即为连通.每次可翻转一个连通块X( ...