Socket编程

Socket基本原理

socket是基于TCP/IP通信的一种抽象,他将TCP/IP协议里面复杂的通信逻辑进行封装

对于web编程来说,只要通过一组简单的API就可以实现网络的连接

而且更多细节在互联网协议已经学过,这里就不再赘述了

socket通信原理

具体可以看一下这张Socket通信图,也就是我们熟知的C/S模型中,socket承担了这样一个接口,用于数据的交互。

经历过小学期的大家应该也都有体会,我们的后台就是服务端,而我们的前端就是客户端。两者之间可以用session来传值,我们同样可以开启线程来实现socket接口。总之它们的作用是一样的。可以类比理解。

在如下的图中,我们可以看到服务端需要绑定,这就涉及到IP地址和端口号,互联网协议也都讲过。

绑定后,服务器进入监听阶段,监听客户端的请求。

当客户端发送请求后,服务器就监听到,这样就建立了链接。

客户端知道链接建立后,通过IO流向socket内发送数据

服务端接受到数据,从socket内读取IO流

然后就是个往复的过程...

 

IO流

因为课程的socket代码中对于IO的操作并不是很高,所以先空下了,有空更新

这里包括IO的使用,包括字符流,字节流

缓冲区

文件等等

Socket应用

我们知道,我们通过socket通信。

而且我们是给socket标记上,端口号,IP地址什么的,才能实现连接,然后进行通信。

Serversocket & Socket

socket分为:ServerSocket(only port number) 和 Socket(IP address & Port number)

分别写在服务器和客户端

这里先说一下区别与联系,以及它们相应的作用

Socket类

java.net.Socket

socket可以使一个应用从网络中读取和写入数据,不同计算机上的两个应用可以通过连接发送和接受字节流。

socket类代表一个客户端套接字,也就是任何连接到一个远程服务器应用时,所构建的服务器。

ServerSocket类

java.net.ServerSocket

我们上面讲,socket时连接到服务器时所构建的socket。那么对于服务器来说,它需要随时待命,因为服务器不知道什么时候,客户端会发来请求。

而ServerSocket就是等待客户端的请求,一旦获得一个连接请求,就创建一个Socket来与客户端进行通信。

我自己是这么理解的(可能有错)

socket是用来连接IO流,接受数据,发送数据的

ServerSocket,是控制这些socket的。也不能叫控制,我举个例子吧

小A和小B用qq聊天。

小A先发起聊天,所以小A创建了一个它的Socket,这个Socket承接着数据被发送到了qq服务器。

qq服务器上的ServerSocket监听到了,于是创建一个对应小A的Socket给了小B

然后小A和小B就靠着这对Socket愉快的聊天了。

所以这个通信的步骤就是:

客户端向服务器发送请求可分为以下步骤: 
1.创建一个Socket实例 
2.利用I/O流与服务器进行通信 
3.关闭socket 
   
服务器接收客户端请求步骤: 

1.创建一个ServerSocket实例,监听客户端发来的请求。 
2.与客户端获取连接后,创建一个Socket实例,利用I/O流与客户端进行通信,完毕后关闭Socket。

其实你也可以理解为啥Socket需要IP的端口,而ServerSocket只需要端口

Socket是拿来传输数据的,在不同的主机之间,所以必须知道主机在哪(IP)以及哪个应用(端口)、

ServerSocket会对应一个具体应用,比如QQ,所以只需要端口。它在这个应用下来处理服务器的Socket并且建立连接。它并不是拿来传数据的。

直接看代码吧还是

实例1

代码都会有点长,但框架基本是一致的,耐心看懂一个后面就好懂了

注意我们前面讲到过,先将服务器绑定进入监听状态,所以你应该先运行服务器的代码,然后在再运行客户端的代码。

否则的话,会直接报错。因为你的客户端请求连接后,发现目的Ip及端口什么都没有(因为你的服务器还没启动),自然就会报连接错误。

代码大概的意思就是

服务端:

这里是服务器,所以new一个ServerSocket,绑定端口号,代表这是这个服务器的socket。打印出来信息只是为了方便可以不印

然后可以看到这是一个try里面套了一个try catch

外面的try将IOException甩锅,写到了main旁边。

注意,这里的Socket s=s.accept()   s是ServerSocket

它调用accept()后,进入阻塞状态一直到连接成功,就会建立一个socket来与客户端通信(关联Thread里的wait notify机制)

里面的try是等到客户端连接后,服务器从socket里面,通过IO流读取数据

IO这两行代码写法很固定,记住就好

这样我们就可以从输出流中通过while循环获取数据,进行我们想要的操作,比如打印

最后先关闭ServerSocket创建的socket,停止通讯

然后关闭ServerSocket,停止监听,服务器也就关闭

import java.io.*;
import java.net.*;

public class Server {
    public static final int PORT=8080;

    public static void main(String[] args) throws IOException {
        ServerSocket s=new ServerSocket(PORT);
        System.out.println("服务器:"+s);


        try {
            Socket socket = s.accept();
            try {
                System.out.println("接受socket连接: " + socket);
                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("END")) break;
                    System.out.println("接受数据: " + str);
                    out.println(str);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("关闭socket连接...");
                socket.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            s.close();
        }
    }
}

客户端:

这里是客户端,这里获取本机IP地址并使用,所以自然是locathost。如果连接别的服务器,指定好IP就行。然后我们new出来Socket。

这个Socket已经被绑定了端口号和IP,所以如果对应的服务器正在监听的话,就可以监听到这个Socket。然后服务器的ServerSocket就会创建一个Socket来与这个Socket进行通信。

也就是我们上面强调的,服务器进入阻塞状态那里。

然后连接成功后,就可以通过IO流向Socket传数据了。

我们用for循环写了十个数字

这个END很重要,也就是这个通讯过程必须得有一个结束标识符,不然可能报错

然后就是关闭socket

import java.net.*;
import java.io.*;

public class Client {
    public static void main(String[] args)throws IOException{
        InetAddress addr = InetAddress.getByName(null);
        System.out.println("addr= "+addr);
        Socket socket = new Socket(addr, Server.PORT);

        try{
            System.out.println("建立socket连接:"+socket);
            BufferedReader in= new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
            for(int i=0;i<10;i++){
                out.println(i);
                String str=in.readLine();
                System.out.println("发送:"+str);
            }
            out.println("END");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("关闭socket连接...");
            socket.close();
        }
    }
}

运行结果就如下,我们先启功服务器,然后运行客户端:

server

 client

 

实例2

多线程版本(虽然它前面的ppt很烂,但是socket的代码还是写的很好的我就基本照搬了)

实例1只是实现了一个客户端和一个服务器,现实中肯定是一个服务器多个客户端

我们利用多线程实现,每一个用户都为其开辟一个线程来进行socket通讯

代码很长,但是细节很多,很nb。因为太长肯定不会考,所以当个兴趣看看就好

import java.io.*;
import java.net.*;

class serverThread extends Thread{
    private Socket socket;
    private BufferedReader in = null;
    private PrintWriter out = null;

    public serverThread(Socket s) throws IOException{
        this.socket=s;
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
    }

    public void run(){
        try{
            while (true){
                String str=in.readLine();
                if(str.equals("END")) break;
                System.out.println("Echoing: "+str);
                out.println(str);
            }
        }catch (IOException e){
            System.out.println("IO Exception");
        }finally {
            try {
                socket.close();
            }catch (IOException e){
                System.err.println("Socket not close");
            }
        }
    }
}

public class Server {
    public static final int PORT=8080;

    public static void main(String[] args) throws IOException {
        ServerSocket s=new ServerSocket(PORT);
        System.out.println("服务器:"+s);
        try {
            while (true){
                Socket socket=s.accept();
                try{
                    serverThread t=new serverThread(socket);
                    t.start();
                }catch (IOException e){
                    socket.close();
                }
            }
        }
        finally {
            s.close();
        }
    }
}
import java.net.*;
import java.io.*;

class clientThread extends Thread{
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;
    private static int counter=0;
    private int id=counter++;
    private  static int threadcount=0;

    public static int threadCount(){
        return threadcount;
    }

    public clientThread(InetAddress addr){
        System.out.println("Making client "+id);
        threadcount++;
        try{
            socket=new Socket(addr,Server.PORT);
        } catch (IOException e) {
            System.out.println("Socket failed");
        }
        try{
            in= new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
        }catch (IOException e1){
            try{
                socket.close();
            }catch (IOException e2){
                System.out.println("Socket not close");
            }
        }
    }

    public void run(){
        try{
            for(int i=0;i<5;i++){
                out.println("Client "+id+": "+i);
                String str =in.readLine();
                System.out.println(str);
            }
            out.println("END");
        }catch (IOException e){
            System.out.println("IO Exception");
        }finally {
            try{
                socket.close();
            }catch (IOException e){
                System.out.println("Socket not close");
            }
            threadcount--;
        }
    }
}

public class Client {
    public static void main(String[] args)throws IOException,InterruptedException{
        InetAddress addr = InetAddress.getByName(null);
        while(true){
            if(clientThread.threadCount()<5){
                clientThread t=new clientThread(addr);
                t.start();
                Thread.currentThread().sleep(100);
            }
        }
    }
}

运行结果就是不停的产生用户,每个用户会发5条消息,不会停下来。但是代码很流畅并不是死循环,因为客户端线程用完就死掉了,这样多线程运作。服务器就可以同时处理好几个客户,而不是一个客户来一次,服务器就开关一次。

实例3

线程池版本

实例2中,每个用户就是一个线程,如果用户很多呢?程序很容易崩溃,而且每个用户占一个线程的话,资源大大浪费。

这就涉及到了线程池技术

有空跟

这个就实现了qq的基本框架,等我考完试学着写个demo发上来

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值