02 客户端

目录

一  介绍Socket

二:Socket对象

  1 创建Socket

   2 常用方法

三  客户端和服务端例子


在Java中使用Socket套接字编写网络通信。Socket是顶层的抽象对象。客户端使用Socket,服务端使用ServerSocket

要实现网络通信有三个要素

 IP地址

 端口号

 协议

一  介绍Socket

 1:Socket可以看成一个操作读/写字节的流。

      流的两端是客户端和服务端,流的通道就是网络中的传输层(如TCP)。流传输的就是数据包。我们操作Socket就是实现服务端和客户端直接的通信。要实现通信有七个步骤

   a:连接远程主机

   b:发送数据

   c:就收数据

   d:关闭数据

   e:绑定端口

    f:监听入站数据

    g:在绑定的端口上接受远程主机的连接

 客户端和服务端都有前四步,后三步是服务端才有

二:Socket对象

  1 创建Socket

Socket类的构造函数

 不同的构造函数,都有一样的功能那就是创建客户端Socket对象,实现连接。

 如 有参构造函数 Socket socket = new Socket(String,int):指定ip,和端口号

 如  无参构造函数 Socket socket = new Socket()

      SocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(), port);

      socket.connect(address )

    使用无参构造函数的优点

    a:可以使用一个socket重复连接 只要设置不同的SocketAddress

    b:关闭socket的时候不需要判断 !=null 当然还有一种方式使用try()关闭的方式

         Socket socket;

          try(socket = new Socket(String,int)){

              }catch(){

               }

     这种方式必须要JDK1.7或者以上版本。而且不需要手动关闭的对象必须实现java.io.Closeable接口 满足这些条件就可以在try()中创建那些自动关闭的对象。

如 使用代理 Socket(Proxy)

   2 常用方法

     获取Socket信息

   

   getInetAddress()和getPort()获取我们要远程连接的主机的地址和端口号

   getLocalAddress()和getLocalPort()获取本地主机的地址和端口号。

  判断连接还是关闭

  

 isConnected():表示是否有过连接,不是表示正在连接。可能有过连接,但是断开了。

 isClosed():表示是否关闭:如果一次都没有连接 这个返回false

 所以万全的判断方式 if(socket. isConnected() && socket.isClosed()){ //连接中}

Socket设置

TcpNoDelay

禁用纳格算法,将数据立即发送出去。纳格算法是以减少封包传送量来增进TCP/IP网络的效能,当我们调用下面代码,如:

Socket socket = new Socket();  
socket.connect(new InetSocketAddress(host, 8000));  
InputStream in = socket.getInputStream();  
OutputStream out = socket.getOutputStream();  
String head = "hello ";  
String body = "world\r\n";  
out.write(head.getBytes());  
out.write(body.getBytes()); 

我们发送了hello,当hello没有收到ack确认(TCP是可靠连接,发送的每一个数据都要收到对方的一个ack确认,否则就要重发)的时候,根据纳格算法,world不会立马发送,会等待,要么等到ack确认(最多等100ms对方会发过来的),要么等到TCP缓冲区内容>=MSS,很明显这里没有机会,我们写了world后再也没有写数据了,所以只能等到hello的ack我们才会发送world,除非我们禁用纳格算法,数据就会立即发送了。

纳格算法参考:http://zh.wikipedia.org/wiki/%E7%B4%8D%E6%A0%BC%E7%AE%97%E6%B3%95

另外有一篇讲解纳格算法和delay ack的文章(挺不错的):http://blog.csdn.net/frankggyy/article/details/6624401  

SoLinger

当我们调用socket.close()返回时,socket已经write的数据未必已经发送到对方了,例如

Socket socket = new Socket();  
socket.connect(new InetSocketAddress(host, 8000));  
InputStream in = socket.getInputStream();  
OutputStream out = socket.getOutputStream();  
String head = "hello ";  
String body = "world\r\n";  
out.write(head.getBytes());  
out.write(body.getBytes()); 
socket.close();

这里调用了socket.close()返回时,hello和world未必已经成功发送到对方了,如果我们设置了linger而不小于0,如:

bool on = true;
int linger = 100;
....
socket.setSoLinger(boolean on, int linger)
......
socket.close();

那么close会等到发送的数据已经确认了才返回。但是如果对方宕机,超时,那么会根据linger设定的时间返回。
UrgentData和OOBInline

TCP的紧急指针,一般都不建议使用,而且不同的TCP/IP实现,也不同,一般说如果你有紧急数据宁愿再建立一个新的TCP/IP连接发送数据,让对方紧急处理。

所以这两个参数,你们可以忽略吧,想知道更多的,自己查下资料。

SoTimeout

设置socket调用InputStream读数据的超时时间,以毫秒为单位,如果超过这个时候,会抛出java.net.SocketTimeoutException。

KeepAlive

keepalive不是说TCP的常连接,当我们作为服务端,一个客户端连接上来,如果设置了keeplive为true,当对方没有发送任何数据过来,超过一个时间(看系统内核参数配置),那么我们这边会发送一个ack探测包发到对方,探测双方的TCP/IP连接是否有效(对方可能断点,断网),在Linux好像这个时间是75秒。如果不设置,那么客户端宕机时,服务器永远也不知道客户端宕机了,仍然保存这个失效的连接。

SendBufferSize和ReceiveBufferSize

TCP发送缓存区和接收缓存区,默认是8192,一般情况下足够了,而且就算你增加了发送缓存区,对方没有增加它对应的接收缓冲,那么在TCP三握手时,最后确定的最大发送窗口还是双方最小的那个缓冲区,就算你无视,发了更多的数据,那么多出来的数据也会被丢弃。除非双方都协商好

当然我们也可以通过getXXX后去对应的参数值

三  客户端和服务端例子

客户端

package com.caoyong.socket;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
/**
 * 
 * Description:客户端
 * @author CaoYong
 * @time 2018年9月4日 下午9:23:55
 */
public class Client {
	
	public void start() {
		//端口号
		Integer port = 8002;
		Socket client = new Socket();
		SocketAddress address;
		OutputStream out;
		try {
			//绑定ip和端口
			address = new InetSocketAddress(InetAddress.getLocalHost(), port);
			//连接 设置连接时间10秒 超过会报异常
			client.connect(address,10000);
			//设置超时时间 10秒
			client.setSoTimeout(10000);
			//获取字节流
			out = client.getOutputStream();
			byte[] b = {11,22,33};
		    out.write(b);
		    //刷新
		    out.flush();
		    //关闭
		    client.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new Client().start();
	}
}

 

服务端

package com.caoyong.socket;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

/**
 * 
 * Description:服务端
 * @author CaoYong
 * @time 2018年9月4日 下午9:24:15
 */
public class Server {
	
	public void start() {
		//端口号
		Integer port =8002;
		//服务端ServerSocket对象
		SocketAddress adderss;
		InputStream in;
		try {
			ServerSocket socket = new ServerSocket();
			adderss = new InetSocketAddress(port);
			//绑定端口
			socket.bind(adderss);
			//接受 并创建Socket
			Socket server = socket.accept();
			//底层流 可以在外层包装其他流
			in = server.getInputStream();
			byte[] info = new byte[3];
			 while(in.read(info) != -1) {
				 for(byte b : info) {
					System.out.println("客户端信息:"+b); 
				 }
			 }
			 //关闭
			 socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new Server().start();
	}
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章