socket编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket编程相关的知识,希望对你有一定的参考价值。

Socket和HTTP的区别:

1. Socket是基于tcp/ip协议,是传输层的连接;而http是基于应用层的连接。

2. HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接,下次建立连接需要tcp重新进行三次握手。因此HTTP连接是一种“短连接。要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”;

由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。

3. HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据;而Socket连接,服务器就可以直接将数据传送给客户端,实现双向通信

Socket优点:传输数据为字节级,传输数据可自定义,数据量小;

传输数据时间短,性能高;

适合C/S之间信息实时交互;

可以加密,数据安全性高

缺点: 需要对传输的数据进行解析,转化为应用层的数据

               相对于Http协议传输,增加了开发量

HTTP优点: 基于应用层的接口使用方便

缺点: 传输速度慢,数据包大。

       如实现实时交互,服务器性能压力大

       数据传输安全性差

应用场景:

Socket适用于对实时性,安全性要求较高,或连接较长的场景,比如网络游戏,银行支付,收发消息。

HTTP适用于对信息传输实时性安全性要求不高,可以一次连接解决的场景,如互联网服务。

Socket

消息目的地:socket地址 = ip地址+端口号

进程间通信需要一对socket,数据在两个Socket间通过IO传输

对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket

套接字之间的连接过程分为三个步骤:

1. 服务器监听ServerSocket将在服务端监听某个端口);

2. 客户端请求(客户端的Socket提出连接请求,指出服务器端套接字的地址和端口号)

3. 连接确认ServerSocket监听到客户端套接字的连接请求,acceptSocket的连接请求,同时在服务端建立一个对应的Socket与之进行通信)

Socket编程有两种通信协议可以选择:

1. UDP 数据报通信

UDP是一种无连接的协议,每次发送数据报时,需要同时发送本机的socket描述符和接收端的socket描述符。

每次通信都需要发送额外的数据。

2. TCP 流通信

TCP是一种基于连接的协议,在使用流通信之前,我们需要在通信的一对socket之间建立连接。其中一个socket作为服务器进行监听连接请求,另一个作为客户端进行连接请求。

区别:

UDP中,数据报大小有64k的限制,而TCP无限制;

UDP不可靠,数据报不一定按照发送顺序被接受,而TCP所有数据按照接受时的顺序读取。

总之TCP适合于如远程登录和文件传输这类的网络服务,因为这些服务需要传输数据大小不确定;

UDP更加简单轻量,用来实现实时性较高或丢包不重要的一些服务。

TCP通信的Java实现:

所有socket相关的类都位于java.net包下。

客户端:

1.创建Socket对象,指明需要连接的服务器的ip地址和端口号

2.连接建立后,通过输出流OutputStreamWriter向服务器端发送请求信息

3.通过输入流获取服务器响应的信息

4.关闭响应资源

Socket client = new Socket(host, port);

Writer writer = new OutputStreamWriter(client.getOutputStream());

writer.write("Hello From Client");

writer.flush();

writer.close();

client.close();

服务器:

1.创建ServerSocket对象,绑定监听端口

2.通过accept()方法监听客户端请求(Accept是阻塞方法,服务器端与客户端建立联系前会一直等待阻塞。)

3.连接建立后,通过输入流读取客户端发送的请求信息

4.通过输出流向客户端发送响应信息

5.关闭相关资源

常见服务模型

1. 阻塞服务器

当一个消息执行发出后,这个消息在发送端的socket中处于排队状态,直到下层网络协议将这些消息发送出去。

消息到达接收端的socket后,也会处于排队状态,直到接收端的进程对这条消息进行了接收处理。

前一个请求没有处理完成,服务器不会处理后面的请求。

ServerSocket server = new ServerSocket(port);

Socket socket = server.accept();

Reader reader = new InputStreamReader(socket.getInputStream());

char chars[] = new char[1024];

int len;

StringBuilder builder = new StringBuilder();

while ((len=reader.read(chars)) != -1) {

     builder.append(new String(chars, 0, len));

}

System.out.println("Receive from client message=: " + builder);

reader.close();

socket.close();

server.close();  // 关闭socket

2. 并发服务器

每接收到一个请求,就启动一个线程处理该请求

这种模式的服务器,好处是不会出现阻塞式服务器请求被拥堵的情况。

存在问题:服务器启动线程有一定开销,请求过多时,将会导致服务器的资源耗尽。

解决:建立线程池来处理请求,每当请求到来时,向线程池申请线程进行处理。

ServerSocket serverSocket = new ServerSocket(SERVER_PORT, 5, serverAddr)

Executor executor = Executors.newFixedThreadPool(100);  

//有客户端向服务器发起tcp连接时,accept会返回一个Socket  

//Socket的対端就是客户端的Socket  

//具体过程可以查看TCP三次握手过程  

Socket connection = serverSocket.accept();    

//利用线程池,启动线程  

executor.execute(new Runnable() {  

@Override  

    public void run() {  

//使用局部引用,防止connection被回收  

     Socket conn = connection;  

        try {  

          InputStream in = conn.getInputStream();  

与阻塞服务器比较,差别仅在于当accept返回socket后启动线程处理,这里使用了Excutor,基于线程池进行处理。

3. 异步服务器

一般借助于系统的异步IO机制(NIO),当一个请求到达时,我们可以先将请求注册,当有数据可以读取时,会得到通知,这时候我们处理请求,这样,服务器进程没有必要阻塞处理,也不会存在很大的系统开销,因此,目前对于并发量要求比较高的服务器,一般都是采用这种方式。

 

 

UDP通信的java实现:

Java通过DatagramPacket类和DatagramSocket类来使用UDP套接字,客户端和服务器端都通过DatagramSocketsend()方法和receive()方法来发送和接收数据,用DatagramPacket来包装需要发送或者接收到的数据。

发送信息时,Java创建一个包含待发送信息的DatagramPacket实例,并将其作为参数传递给DatagramSocket实例的send()方法;接收信息时,Java程序首先创建一个DatagramPacket实例,该实例预先分配了一些空间,并将接收到的信息存放在该空间中,然后把该实例作为参数传递给DatagramSocket实例的receive()方法。

在创建DatagramPacket实例时,要注意:如果该实例用来包装待接收的数据,则不指定数据来源的远程主机和端口,只需指定一个缓存数据的byte数组即可(在调用receive()方法接收到数据后,源地址和端口等信息会自动包含在DatagramPacket实例中),而如果该实例用来包装待发送的数据,则要指定要发送到的目的主机和端口。

客户端:

1. 定义发送信息

2. 创建DatagramPacket,包含将要发送的信息

3. 创建DatagramSocket(可以有选择地对本地地址和端口号进行设置)

4. 发送数据

//1、定义服务器的地址、端口号、数据

InetAddress address =InetAddress.getByName("localhost");

int port =10010;

byte[] data ="用户名:admin;密码:123".getBytes();

//2、创建数据报,包含发送的数据信息

DatagramPacket packet = newDatagramPacket(data,data,length,address,port);

//3、创建DatagramSocket对象

DatagramSocket socket =newDatagramSocket();

//4、向服务器发送数据

socket.send(packet);

 

//接受服务器端响应数据

//======================================

//1、创建数据报,用于接受服务器端响应数据

byte[] data2 = new byte[1024];

DatagramPacket packet2 = new DatagramPacket(data2,data2.length);

//2、接受服务器响应的数据

socket.receive(packet2);

String raply = new String(data2,0,packet2.getLenth());

System.out.println("我是客户端,服务器说:"+reply);

//4、关闭资源

socket.close();

服务器:

1. 创建DatagramSocket,指定端口号

2. 创建DatagramPacket

3. 接受客户端发送的数据信息

4. 读取数据

//服务器端,实现基于UDP的用户登录

//1、创建服务器端DatagramSocket,指定端口,UDP服务器为所有通信使用同一套接字

DatagramSocket socket =new datagramSocket(10010);

//2、创建数据报,用于接受客户端发送的数据

byte[] data =newbyte[1024];//

DatagramPacket packet =newDatagramPacket(data,data.length);

//3、接受客户端发送的数据

socket.receive(packet); //此方法在接受数据报之前会一直阻塞

//4、读取数据

String info =newString(data,o,data.length);

System.out.println("我是服务器,客户端告诉我"+info);

 

//=========================================================

//向客户端响应数据

//1、定义客户端的地址、端口号、数据

InetAddress address = packet.getAddress();

int port = packet.getPort();

byte[] data2 = "欢迎您!".geyBytes();

//2、创建数据报,包含响应的数据信息

DatagramPacket packet2 = new DatagramPacket(data2,data2.length,address,port);

//3、响应客户端

socket.send(packet2);

//4、关闭资源

socket.close();

由于UDP协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在receive()方法处,这样客户端将永远都接收不到服务器端发送回来的数据,但是又没有任何提示。为了避免这个问题,我们在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

int TIMEOUT = 5000;  //设置接收数据的超时时间

//客户端在9000端口监听接收到的数据

DatagramSocket ds = new DatagramSocket(9000);

ds.setSoTimeout(TIMEOUT);    


以上是关于socket编程的主要内容,如果未能解决你的问题,请参考以下文章

C++ socket编程 和 MFC socket编程 有啥区别??

socket编程为啥要用wsastartup

Socket编程

Socket编程通过Socket实现TCP编程

Socket网络编程

iOS网络编程笔记——Socket编程