[iOS进阶]Socket 网络编程

文章参考:http://blog.csdn.net/liulanghk/article/details/46325861

1.TCP/IP、UDP

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
下面这张图,表明了这些协议的关系,如图TCP/IP协议族包括运输层、网络层、链路层:
协议关系

这里写图片描述

2.Socket

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

这里写图片描述

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

3.使用socket

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

客户端:

  1. 用服务器的IP地址和端口号实例化Socket对象。

  2. 调用connect方法,连接到服务器上。

  3. 将发送到服务器的IO流填充到IO对象里,比如BufferedReader/PrintWriter。

  4. 利用Socket提供的getInputStream和getOutputStream方法,通过IO流对象,向服务器发送数据流。

  5. 通讯完成后,关闭打开的IO对象和Socket。

服务器:

  1. 在服务器,用一个端口来实例化一个 ServerSocket对象。此时,服务器就可以这个端口时刻监听从客户端发来的连接请求。

  2. 调用ServerSocket的accept方法,开始监听连接从端口上发来的连接请求。   

  3. 利用accept方法返回的客户端的Socket对象,进行读写IO的操作

  4. 通讯完成后,关闭打开的流和Socket对象。

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

4.代码示例

参考文章: http://www.blogjava.net/Reg/archive/2010/07/17/326392.html

服务器端的代码

根据上面描述的通讯流程,我们可以按如下的步骤设计服务器端的代码。

  • 第一步,依次点击Eclipse环境里的“文件”|“新建”|“项目”选项,进入“新建项目”的向导对话框,在其中选中“Java项目”,点击“下一步”按钮,在随后弹出的对话框里,在其中的“项目名”一栏里,输入项目名“TCPSocket”,其它的选项目,选择系统默认值,再按“完成”按钮,结束创建Java项目的动作。

  • 第二步,完成创建项目后,选中集成开发环境左侧的项目名“TCPSocket”,点击右键,在随后弹出的菜单里依次选择“新建”!“类”的选项,创建服务器类的代码。在随后弹出的“新建Java类”的对话框里,输入包名“tcp”,输入文件名“ServerCode”,请注意大小写,在“修饰符”里选中“公用”,在“想要创建哪些方法存根”下,选中“public static void main(String[] args )”单选框,同时把其它两项目取消掉,再按“完成”按钮,可以生成代码。

  • 第三步,在生成的代码里,编写引入Java包的代码,只有当我们 引入这些包后,我们才能调用这些包里提供的IO和Socket类的方法。

  • 第四步,编写服务器端的主体代码,如下所示。

package tcp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerCode

{
    // 设置端口号   
    public static int portNo = 3333;
    public static void main(String[] args) throws IOException
    {
        ServerSocket s = new ServerSocket(portNo);   
        System.out.println("The Server is start: " + s);
        // 阻塞,直到有客户端连接     
        Socket socket = s.accept();
        try
        { 
            System.out.println("Accept the Client: " + socket); 
            //设置IO句柄
            BufferedReader in = new 
            BufferedReader(new InputStreamReader(socket.getInputStream()));                                                                                                                                      

            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
            while (true)   
            {
                String str = in.readLine();
                if (str.equals("byebye"))
                {
                    break;
                }
                System.out.println("In Server reveived the info: " + str);
                out.println(str);
            }
        } 
        finally 
        { 
            System.out.println("close the Server socket and the io.");
            socket.close();
            s.close();
        }
    }
}

这段代码的主要业务逻辑是:

  1. 在上述代码里的main函数前,我们设置了通讯所用到的端口号,为3333。
  2. 在main函数里,根据给定3333端口号,初始化一个ServerSocket对象s,该对象用来承担服务器端监听连接和提供通讯服务的功能。
  3. 调用ServerSocket对象的accept方法,监听从客户端的连接请求。当完成调用accept方法后,整段服务器端代码将回阻塞在这里,直到客户端发来connect请求。
  4. 当客户端发来connect请求,或是通过构造函数直接把客户端的Socket对象连接到服务器端后,阻塞于此的代码将会继续运行。此时服务器端将会根据accept方法的执行结果,用一个Socket对象来描述客户端的连接句柄。
  5. 创建两个名为in和out的对象,用来传输和接收通讯时的数据流。
  6. 创建一个while(true)的死循环,在这个循环里,通过in.readLine()方法,读取从客户端发送来的IO流(字符串),并打印出来。如果读到的字符串是“byebye”,那么退出while循环。
  7. 在try…catch…finally语句段里,不论在try语句段里是否发生异常,并且不论这些异常的种类,finally从句都将会被执行到。在finally从句里,将关闭描述客户端的连接句柄socket对象和ServerSocket类型的s对象。

客户端代码

  • 第一步,在TCPSocket项目下的tcp包下,创建一个名为ClientCode.java的文件。在其中编写引入Java包的代码,如下所示:

  • 第二步,编写客户端的主体代码,如下所示:

package tcp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class ClientCod
{
    static String clientName = "Mike";
    //端口号
    public static int portNo = 3333;
    public static void main(String[] args) throws IOException
    {
        // 设置连接地址类,连接本地
        InetAddress addr = InetAddress.getByName("localhost");
        //要对应服务器端的3333端口号
        Socket socket = new Socket(addr, portNo);
        try
        {
            System.out.println("socket = " + socket);
            // 设置IO句柄
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
            out.println("Hello Server,I am " + clientName);
            String str = in.readLine();
            System.out.println(str);
            out.println("byebye");
        }
        finally
        {
            System.out.println("close the Client socket and the io.");
            socket.close();
        }
    }
}

上述客户端代码的主要业务逻辑是:

  1. 同样定义了通讯端口号,这里给出的端口号必须要和服务器端的一致。
  2. 在main函数里,根据地址信息“localhost”,创建一个InetAddress类型的对象addr。这里,因为我们把客户端和服务器端的代码都放在本机运行,所以同样可以用“127.0.0.1”字符串,来创建InetAddress对象。
  3. 根据addr和端口号信息,创建一个Socket类型对象,该对象用来同服务器端的ServerSocket类型对象交互,共同完成C/S通讯流程。
  4. 同样地创建in和out两类IO句柄,用来向服务器端发送和接收数据流。
  5. 通过out对象,向服务器端发送”Hello Server,I am …”的字符串。发送后,同样可以用in句柄,接收从服务器端的消息。
  6. 利用out对象,发送”byebye”字符串,用以告之服务器端,本次通讯结束。
  7. 在finally从句里,关闭Socket对象,断开同服务器端的连接。

运行效果

在上述两部分里,我们分别讲述了C/S通讯过程中服务器端和客户端代码的业务逻辑,下面我们将在集成开发环境里,演示这里通讯流程。

  • 第一步,选中ServerCode.java代码,在eclipse的“运行”菜单里,选中“运行方式”|“1 Java应用程序”的菜单,开启服务器端的程序。
    开启服务端程序后,会在eclipse环境下方的控制台里显示如下的内容:
    The Server is start: ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=3333]
    在这里,由于ServerSocket对象并没监听到客户端的请求,所以addr和后面的port值都是初始值。

  • 第二步,按同样的方法,打开ClientCode.java程序,启动客户端。启动以后,将在客户端的控制台里看到如下的信息:
    socket = Socket[addr=localhost/127.0.0.1,port=3333,localport=1326]
    Hello Server,I am Mike
    close the Client socket and the io.
    从中可以看到,在第一行里,显示客户端Socket对象连接的IP地址和端口号,在第二行里,可以到到客户端向服务器端发送的字符串,而在第三行里,可以看到通讯结束后,客户端关闭连接Socket和IO对象的提示语句。

  • 第三步,在eclipse下方的控制台里,切换到ServerCode服务端的控制台提示信息里,我们可以看到服务器端在接收到客户端连接请求后的响应信息。
    响应的信息如下所示:
    The Server is start: ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=3333]
    Accept the Client: Socket[addr=/127.0.0.1,port=1327,localport=3333]
    In Server reveived the info: Hello Server,I am Mike
    close the Server socket and the io.
    其中,第一行是启动服务器程序后显示的信息。在第二行里,显示从客户端发送的连接请求的各项参数。在第三行里,显示了从客户端发送过来的字符串。在第四行里,显示了关闭服务器端ServerSocket和IO对象的提示信息。从中我们可以看出在服务器端里accept阻塞和继续运行的这个过程。

通过上述的操作,我们可以详细地观察到C/S通讯的全部流程,请大家务必要注意:一定要先开启服务器端的程序再开启客户端,如果这个步骤做反的话,客户端程序会应找不到服务器端而报异常。

5. 延伸阅读

1.Linux socket 编程

2. 网络编程学习笔记一:Socket编程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值