Java编程之揭开Socket编程的神秘面纱

对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。那么我想问:

  1. 什么是TCP/IP、UDP?
  2. Socket在哪里呢?
  3. Socket是什么呢?
  4. 你会使用它们吗?

什么是TCP/IP、UDP?

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。

这里有一张图,表明了这些协议的关系:
这里写图片描述

TCP/IP协议族包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。

Socket在哪里呢?

在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。
这里写图片描述

原来Socket在这里。

Socket是什么呢?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

你会使用它们吗?

前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

这里写图片描述

先从服务器端说起:“服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束”

在这里我就举个简单的例子,我们走的是TCP协议这条路(见图2):

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.text.MessageFormat;

/**
 * 服务器端
 * Created by ZaneLove on 2015/3/12.
 */
public class TCPEchoServer {
    private static final int BUFSIZE = 32;

    public static void main(String[] args) throws IOException {
        //从控制台获取需要监听的端口号
        if(args.length != 1) {
            throw new IllegalArgumentException("Parameter(s):<Port>");
        }
        //获取端口号
        int serverPort = Integer.parseInt(args[0]);
        //实例化一个ServerSocket
        ServerSocket serverSocket = new ServerSocket(serverPort);
        System.out.println(MessageFormat.format("开始启动监听,端口号:{0}",args[0]));
        //初始接收数据的总字节数
        int recvMsgSize;
        //接收数据的缓冲区
        byte[] receiveBuf = new byte[BUFSIZE];
        //循环迭代,监听端口号,处理新的连接请求
        while(true) {
            //阻塞等待,每接收到一个请求就创建一个新的连接实例
            Socket cIntSocket = serverSocket.accept();
            //获取连接的客户端的SocketAddress
            SocketAddress clientAddress = cIntSocket.getRemoteSocketAddress();
            //打印输出连接客户端地址信息
            System.out.println("Handing client at"+clientAddress);
            //从客户端接收数据的对象
            InputStream in = cIntSocket.getInputStream();
            //向客户端发送数据的对象
            OutputStream out = cIntSocket.getOutputStream();
            //读取客户端发送的数据后,再发送到客户端
            while((recvMsgSize = in.read(receiveBuf))!=-1) {
                out.write(receiveBuf,0,recvMsgSize);
            }
            //客户端关闭连接时,关闭连接
            System.out.println("客户端关闭连接");
            cIntSocket.close();
        }
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;

/**
 * 客户端
 * Created by ZaneLove on 2015/3/12.
 */
public class TCPEchoClient {
    public static void main(String[] args) throws IOException {
        //判断从控制台接受的参数是否正确
        if(args.length < 2 || args.length > 3) {
            throw new IllegalArgumentException("Parameter(s)<Server><Word>[<Port>]");
        }
        //获取服务器地址
        String server = args[0];
        //获取需要发送的信息
        byte[] data = args[1].getBytes();
        //如果有三个从参数那么就获取发送信息的端口号,默认端口号为8099
        int serverPort = (args.length == 3)?Integer.parseInt(args[2]):8099;
        //根据服务器地址和端口号实例化一个Socket实例
        Socket socket = new Socket(server,serverPort);
        System.out.println("Connected to server ... sending echo string");
        //返回此套接字的输入流,即从服务器接受的数据对象
        InputStream in = socket.getInputStream();
        //返回此套接字的输出流,即向服务器发送的数据对象
        OutputStream out = socket.getOutputStream();
        //向服务器发送从控制台接收的数据
        out.write(data);
        //接收数据的计数器,将写入数据的初始偏移量
        int totalBytesRcvd = 0;
        //初始化接收数据的总字节数
        int bytesRcvd;
        while(totalBytesRcvd < data.length) {
            //服务器关闭连接,则返回-1,read方法返回接收数据的总字节数
            if((bytesRcvd = in.read(data,totalBytesRcvd,data.length-totalBytesRcvd))==-1) {
                throw  new SocketException("与服务器连接已关闭");
            }
        totalBytesRcvd += bytesRcvd;
        }
        //打印服务器发送来的数据
        System.out.println("Received:"+new String(data));
        //关闭连接
        socket.close();
    }
}

首先运行服务器端,监听8099端口:

这里写图片描述

接着运行客户端程序,并且向服务器端发送消息:

这里写图片描述

再次查看我们的服务器端控制台,我们可以看到前面客户端连接的地址信息:

这里写图片描述

TCP/IP、HTTP、Socket的区别?

  • 网络层层面不一样:
    • IP协议属于网络层
    • TCP协议属于基于网络层IP协议的传输层
    • HTTP协议属于基于传输层的TCP协议的应用层
    • Socket则是对TCP/IP协议的封装和应用,处于应用层和传输层之间的Socket抽象层(不算是协议,它只是提供了一组针对TCP或者UDP编程的接口)
  • 另外:

    • TCP连接需要3次握手,断开连接需要4次握手
    • HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接,属于一次性的短连接
  • TCP和UDP的区别?

    • TCP是面向连接的,属于可靠性协议(虽然说网络的不安全不稳定特性决定了多少次握手都不能保证连接的可靠性,但TCP的三次握手在最低限度上保证了连接的可靠性),所以传输速度慢,但传输时可靠,适合传输大量数据,应用于远程登录,文件传输
    • UDP不是面向连接的,不与对方建立连接,而是直接就把数据包发送过去,属于是不可靠的一种数据传输协议,但实时性比较强,效率比较高

扩展学习:
①、ISO/OSI网络体系结构和TCP/IP协议模型:http://blog.csdn.net/htyurencaotang/article/details/11473015
②、多线程编程那些事:http://blog.csdn.net/feixiaoxing/article/category/935718

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值