socket基础案例

socket原理

Socket协议的形象描述
  socket的英文原义是“孔”或“插座”。在这里作为4BDS UNIX的进程通信机制,取后一种意思。socket非常类似于电话插座。以一个国家级电话网为例。电话的通话双方相当于相互通信的2个进程,区号是它的网络地址;区内一个单位的交换机相当于一台主机,主机分配给每个用户的局内号码相当于socket号。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求(假如对方不在同一区内,还要拨对方区号,相当于给出网络地址)。对方假如在场并空闲(相当于通信的另一主机开机且可以接受连接请求),拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。


通常短连接是这样:连接->传输数据->关闭连接
那什么是长连接?一般长连接相对短连接而言的,长连接在传输完数后不关闭连接,而不断的发送包保持连接等待处理下一个数据包。
such as: 连接->传输数据->保持连接 -> 传输数据-> 。。。 ->关闭连接。

那什么时候用短连接呢?
一般长连接用于少数client-end to server-end的频繁的通信,例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。
而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源。(查看来源)


TCP/IP通信解读长短链接

一。通信方式
主要有以下三大类:
(一)SERVER/CLIENT方式
1.一个Client方连接一个Server方,或称点对点(peer to peer):
2.多个Client方连接一个Server方,这也是通常的并发服务器方式。
3.一个Client方连接多个Server方,这种方式很少见,主要
用于一个客户向多个服务器发送请求情况。

(二)连接方式
1.长连接
Client方与Server方先建立通讯连接,连接建立后不断开,
然后再进行报文发送和接收。这种方式下由于通讯连接一直
存在,可以用下面命令查看连接是否建立:
netstat –f inet|grep 端口号(如5678)。
此种方式常用于点对点通讯。

2.短连接
Client方与Server每进行一次报文收发交易时才进行通讯连
接,交易完毕后立即断开连接。此种方式常用于一点对多点
通讯,比如多个Client连接一个Server.

(三)发送接收方式
1.异步
报文发送和接收是分开的,相互独立的,互不影响。这种方
式又分两种情况:
(1)异步双工:接收和发送在同一个程序中,有两个不同的
子进程分别负责发送和接收
(2)异步单工:接收和发送是用两个不同的程序来完成。
2.同步
报文发送和接收是同步进行,既报文发送后等待接收返回报文。
同步方式一般需要考虑超时问题,即报文发上去后不能无限等
待,需要设定超时时间,超过该时间发送方不再等待读返回报
文,直接通知超时返回。

实际通信方式是这三类通信方式的组合。比如一般书上提供的
TCP/IP范例程序大都是同步短连接的SERVER/CLIENT程序。有的
组合是基本不用的,比较常用的有价值的组合是以下几种:

同步短连接Server/Client
同步长连接Server/Client
异步短连接Server/Client
异步长连接双工Server/Client
异步长连接单工Server/Client

其中异步长连接双工是最为复杂的一种通信方式,有时候经
常会出现在不同银行或不同城市之间的两套系统之间的通信。
比如金卡工程。由于这几种通信方式比较固定,所以可以预
先编制这几种通信方式的模板程序。

二.报文格式
通信报文格式多样性更多,相应地就必须设计对应的读写报文的接
收和发送报文函数。

(一)阻塞与非阻塞方式 
1.非阻塞方式
读函数不停地进行读动作,如果没有报文接收到,等待一段时间后
超时返回,这种情况一般需要指定超时时间。
2.阻塞方式
如果没有报文接收到,则读函数一直处于等待状态,直到有报文到达。

(二)循环读写方式
1.一次直接读写报文
在一次接收或发送报文动作中一次性不加分别地全部读取或全部
发送报文字节。
2.不指定长度循环读写
这一般发生在短连接进程中,受网络路由等限制,一次较长的报
文可能在网络传输过程中被分解成了好几个包。一次读取可能不
能全部读完一次报文,这就需要循环读报文,直到读完为止。

3.带长度报文头循环读写
这种情况一般是在长连接进程中,由于在长连接中没有条件能够
判断循环读写什么时候结束,所以必须要加长度报文头。读函数
先是读取报文头的长度,再根据这个长度去读报文.实际情况中,
报头的码制格式还经常不一样,如果是非ASCII码的报文头,还必须
转换成ASCII,常见的报文头码制有:
(1)n个字节的ASCII码
(2)n个字节的BCD码
(3)n个字节的网络整型码

------------------------------------------------------------------------------------------------

基于TCP/IP的通讯有两个步骤:1、连接;2、通讯。client连接到server的监听端口,随后在server端会随机产生一个通讯端口和client进行数据通讯。端口是个unsigned short类型的数据结构。所以最多连接是65535。系统一般要求1024前的端口是系统使用的。建议用户程序的监听端口大于10000,通讯端口是系统随机分配的。假如需要有10万个客户端,程序应该怎么处理呢? 如果只有一个服务器,只能用短连接的方式处理。如果都是长连接就要负载均衡。把连接分到其它服务器上去处理。

 

Socket案例如下

 

客户端代码:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class ClintSocketTest  {
 public void clintStart(){ 
  
 try{
    //Socket socket = new Socket("130.251.9.34", 8787); 

 //Socket中的2个参数分别是服务器地址和开放的端口号
  Socket socket = new Socket("localhost", 8787);
    System.out.println("Established a connection...");
    
    // 由系统标准输入设备构造BufferedReader对象
    BufferedReader sysin = new BufferedReader(new InputStreamReader(System.in));

    // 由Socket对象得到输出流,并构造PrintWriter对象
    PrintWriter out = new PrintWriter(socket.getOutputStream());

    // 由Socket对象得到输入流,并构造相应的BufferedReader对象
    BufferedReader in = new BufferedReader(new InputStreamReader(socket
      .getInputStream()));

    String line; // 保存一行内容

    // 从系统标准输入读入一字符串
    line = sysin.readLine();

    while (!line.equals("bye")) { // 若从标准输入读入的字符串为 "bye"则停止循环

     // 将从系统标准输入读入的字符串输出到Server
     out.println(line);

     // 刷新输出流,使Server马上收到该字符串
     out.flush();

     // 在系统标准输出上打印读入的字符串
     System.out.println("[Client]: " + line);

     // 从Server读入一字符串,并打印到标准输出上
     System.out.println("[Server]: " + in.readLine());

     // 从系统标准输入读入一字符串
     line = sysin.readLine();

    }

    out.close(); // 关闭Socket输出流
    in.close(); // 关闭Socket输入流
    socket.close(); // 关闭Socket
   } catch (Exception e) {
    System.out.println("Error. " + e);
   }
  }
 
 public static void main(String[] args)
 {
  ClintSocketTest a = new ClintSocketTest();
  a .clintStart();
  
 
 }
}

 

 

服务端代码:


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class ServerSocketTest {

 public void serverStart() {
  try {
   ServerSocket sc = new ServerSocket(8787);
   Socket ss = sc.accept();
   while(true){
    //由socket对象得到输入流,构造BufferedReader对象
    BufferedReader is=new BufferedReader(new InputStreamReader(ss.getInputStream()));
       //由socket对象得到输出流,构造PrintWriter对象
    PrintWriter os=new PrintWriter(ss.getOutputStream());
    //由系统标准输入设备,构造BufferedReader对象
    BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
    //从标准输入上打印客户端读入的字符串
    System.out.println("Client:"+is.readLine());
    //从标准输入读入一个字符串
    String line=null;
    line=sin.readLine();
   //如果读取字符串为bye 则停止循环
    while(!line.equals("bye")){
     //向客户端输出该字符
     os.println(line);
     os.flush();
    }
    is.close();
    os.close();
    ss.close();

   }
   
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 public static void main(String[] args)
 {
  ServerSocketTest serSocket = new ServerSocketTest();
  serSocket.serverStart();
  

 }
}

 

你可能感兴趣的:(socket)