Java网络编程如何初始化套接字

Python017

Java网络编程如何初始化套接字,第1张

不同的构造方法不仅带的参数不同,所具有的意义也不一样。下面分别解析这两个类的实例初始化过程。 ServerSocket 实例的初始化 ServerSocket 类提供了四个构造器: public Socket(String host, int port) throws UnknownHostException, IOException public Socket(InetAddress address, int port) throws IOException public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException public Socket() public Socket(Proxy proxy) 带参构造器用来创建已绑定的服务器套接字,也就是说构造成功后它就已经开始侦听指定的端口,且能够调用 accept() 方法来接受客户端连接。默认构造器则会创建未绑定的服务器套接字,构造成功后必须手动将其绑定到一个本地地址才能用,在绑定之前可以进行一些选项配置。 带参构造器 总的来说,带参构造器提供了三个参数: port 指定该服务器套接字所要侦听的本地端口。如果为 0,则由系统自动分配一个端口号,这必须以另外的方式让客户端获取端口号。 backlog 这个名词目前还没有合适的译名。底层系统的 TCP 实现会维护一个连接队列,该队列缓存了已被 TCP 处理完毕,但还没有被服务器套接字接受的客户端连接。一旦某个连接被接受(通过调用 accept() 方法),它就会被从队列中移除。backlog 参数就用于指定队列的最大长度,默认值为 50,但这个值只是一个建议,底层系统可能根据需要自动调整。如果队列满了,则其行为是平台相关的:微软的 WINSOCK 会拒绝新的连接,其他实现则什么都不做。严格地说,微软没有遵守规范,破坏了游戏规则…… bindAddr 一台机器可能会有多个本地 IP 地址,例如同时使用多块网卡。使用其他两个带参构造器时,该参数为 null,服务器套接字会在所有的本地 IP 地址(0.0.0.0 或 ::0)上侦听。如果希望只侦听一个地址,则可使用该参数。 默认构造器 如果使用默认构造器,在绑定地址前,还可以做些配置。绑定操作由两个 bind 方法定义,参数类似于带参构造器。配置项包括以下方面(都必须在绑定前配置): 设置是否重用本地地址 该选项由 setReuseAddress(boolean on) 方法配置,对应底层系统的 SO_REUSEADDR 套接字选项。JDK 没有定义该选项的默认值。如果该选项为 false,则在关闭 TCP 连接时,为了保证可靠性,该连接可能在关闭后的一段时间(大约两分钟)内保持超时状态(通常称为 TIME_WAIT 状态或 2MSL 等待状态),这段时间里无法将新建的服务器套接字绑定到同一个地址。在开发阶段,服务器可能不断重启,打开改选项会非常有用。 设置接收缓冲区大小 该选项由 setReceiveBufferSize(int size) 方法配置,对应底层系统的 SO_RCVBUF 套接字选项,单位是字节。《RFC 1323 - TCP Extensions for High Performance》将缓冲区大小定义为 64KB。该选项只是一个建议值,底层系统可能根据需要自行调整。 设置超时值 该选项由 setSoTimeout(int timeout) 方法配置,对应底层系统的 SO_TIMEOUT 套接字选项,单位是毫秒。默认值为 0。该选项影响 accept 方法的阻塞时间长度,如果超时将引发 SocketTimeoutException。如果设为 0,则表示永不超时。 设置性能首选项 性能首选项包括连接时间、延迟和带宽三个选项,由 setPerformancePreferences(int connectionTime, int latency, int bandwidth) 方法配置。这三个数值分别表示短连接时间、低延迟和高带宽的相对重要性,数值越大则越重要其各自的绝对值没有意义。该方法的初衷是为了让 Java 能在用非 TCP/IP 实现的套接字环境下工作得更好,某些需要对网络进行调优的程序也可以将这三个首选项作为配置参数提供给用户。 Socket 实例的初始化 Socket 类提供了六个公共构造器(已过时的除外): public Socket(String host, int port) throws UnknownHostException, IOException public Socket(InetAddress address, int port) throws IOException public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException public Socket() public Socket(Proxy proxy) 前四个构造器创建已连接的客户端套接字,也就是说构造的时候就会去连接服务器。前两个构造器需要提供服务器的地址和端口作为参数,本地地址和端口由系统自动分配后两个允许手动指定本地地址和端口,但极少使用。后两个构造器创建未连接的套接字,创建后需要调用 connect 方法手动连接,连接之前可以做一些配置。最后一个构造器接受一个代表代理服务其的 Proxy 对象,JDK 支持 HTTP 和 SOCKS(V4 或 V5)两种代理类型。 连接前的配置 在连接前,客户端套接字不仅像服务器套接字那样可以设置是否重用本地地址、缓冲区大小、超时值和性能首选项,还能够配置以下各项(都必须在连接前配置): 设置是否保持活跃 该选项由 setKeepAlive(boolean on) 方法配置,对应底层系统的 SO_KEEPALIVE 套接字选项。默认值为 false。如果打开该选项,则套接字会定期自动发送保持活跃的探测性消息,类似于心跳检测。根据《RFC 1122 - Requirements for Internet Hosts》的规定,保持活跃机制只是 TCP 的一个可选功能,如果支持的话,默认必须为 false,而且这种机制默认在成功建立连接后,且连续两小时没有数据传输的情况下才会被激活。从另一方面来看,通过套接字的 I/O 操作完全可以知道连接是否还有效,所以该选项的实用价值不大。 设置是否收发带外数据 该选项由 setOOBInline(boolean on) 方法配置,对应底层系统的 SO_OOBINLINE 套接字选项。默认值为 off。带外数据(Out-of-band Data)也叫做紧急数据,表示数据很重要,需要使用不同于发送普通数据的一个专用通道来发送。打开该选项后,就可以调用 sendUrgentData(int data) 方法发送一个字节的紧急数据。JDK 对带外数据只提供了有限支持,紧急数据将会和普通数据一起被收到,并且无法自动区分。该选项对应用开发人员意义不大。 设置是否从容关闭连接 该选项由 setSoLinger(boolean on, int linger) 方法配置,对应底层系统的 SO_LINGER 套接字选项。默认为 false。该选项只会影响套接字的关闭,其中的 linger 参数表示超时时间,单位为秒。如果打开改选项:如果将 linger 设为 0,则关闭套接字的时候,未发送的数据会被丢弃,且另一端会出现连接被同位体重置的异常如果 linger 非 0,则关闭套接字的线程将被阻塞,直到数据全部发送或超时,超时后的行为与底层系统相关,JDK 无法控制。如果关闭该选项,则套接字正常关闭,数据也会全部发送。由于底层实现的差异性,不提倡应用开发人员打开该选项。 设置是否延迟发送数据 该选项由 setTcpNoDelay(boolean on) 方法配置,对应底层系统的 TCP_NODELAY TCP 选项。默认值为 off。打开该选项将禁用 Nagle 算法,TCP 包会立即发送关闭该选项则会启用 Nagle 算法,多个较小的 TCP 包会被组合成一个大包一起发送,虽然发送延迟了,但有利于避免网络拥塞。默认为 false。该选项对实时性很强的程序可能有用,但一般的程序不需要关心。 设置流量类别 该选项由 setTrafficClass(int tc) 方法配置,对应底层系统的“流量类别”套接字属性。该选项用于向网络(例如路由器)提示从该套接字发送的包需要获取哪些服务类型,对本地 TCP 协议栈没有影响。IPv4 和 IPv6 分别定义了多个不同的值,例如 IPv4 将 0x08 定义为最大吞吐量,0x10 定义为最小延迟,等等。可以用或运算将多个值合并为一个选项。该选项用来调整性能,需要根据实际情况设置。由于只是建议值,可能被网络忽略。

Server端:

import java.io.*

import java.net.*

import java.applet.Applet

public class TalkServer{

public static void main(String args[]) {

try{

ServerSocket server=null

try{

server=new ServerSocket(4700)

}catch(Exception e) {

System.out.println("can not listen to:"+e)

}

Socket socket=null

try{

socket=server.accept()

}catch(Exception e) {

System.out.println("Error."+e)

}

String line

BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()))

PrintWriter os=new PrintWriter(socket.getOutputStream())

BufferedReader sin=new BufferedReader(new InputStreamReader(System.in))

System.out.println("Client:"+is.readLine())

line=sin.readLine()

while(!line.equals("bye")){

os.println(line)

os.flush()

System.out.println("Server:"+line)

System.out.println("Client:"+is.readLine())

line=sin.readLine()

}

os.close()

is.close()

socket.close()

server.close()

}catch(Exception e){

System.out.println("Error:"+e)

}

}

}

Client端:

import java.io.*

import java.net.*

public class TalkClient {

public static void main(String args[]) {

try{

Socket socket=new Socket("127.0.0.1",4700)

BufferedReader sin=new BufferedReader(new InputStreamReader(System.in))

PrintWriter os=new PrintWriter(socket.getOutputStream())

BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()))

String readline

readline=sin.readLine()//从系统标准输入读入一字符串

while(!readline.equals("bye")){

os.println(readline)

os.flush()

System.out.println("Client:"+readline)

System.out.println("Server:"+is.readLine())

readline=sin.readLine()//从系统标准输入读入一字符串

}

os.close()//关闭Socket输出流

is.close()//关闭Socket输入流

socket.close()//关闭Socket

}catch(Exception e) {

System.out.println("Error"+e)//出错,则打印出错信息

}

}

}

2.百度百科搜索很详细

3.附件中的代码仅供参考

TcpSocketServerDemo.java

package yaoshun.InetAddress

import java.io.DataInputStream

import java.io.DataOutputStream

import java.io.IOException

import java.net.ServerSocket

import java.net.Socket

import java.util.ArrayList

import java.util.List

import java.util.Random

/*

 * 客户端:TCP服务器端

 */

public class TcpSocketServerDemo {

private ServerSocket serverSocket// 服务器连接

private int port // 服务器监听端口

private List<Socket> sockets

private int ranNumber

/**

 * 服务器结束的标志

 */

public boolean ISEND

public TcpSocketServerDemo(int port) {

this.port = port

serverSocket = null

sockets = new ArrayList<Socket>()

ranNumber = new Random().nextInt(50)

}

/*

 * 启动服务器

 */

public void starServer() {

try {

// 第一步:建立服务器连接(绑定监听端口)

serverSocket = new ServerSocket(port)

System.out.println("姚舜的服务器已经启动...")

Socket socket// 客户端连接(如果建立连接时,本机网络不容或服务器程序未启动则会抛出异常)

ServerThread serverThread

Thread t2

while (!ISEND) {

// 第二步:监听端口(获取客户端连接)

socket = serverSocket.accept()

if (ISEND) {

while (!socket.isClosed()) {

socket.close()

Thread.sleep(100)

}

} else {

sockets.add(socket)

serverThread = new ServerThread(this, socket)

t2 = new Thread(serverThread)

t2.start()

}

}

for (Socket s : sockets) {

s.close()

}

serverSocket.close()

} catch (Exception e) {

e.printStackTrace()

}

}

public static void main(String[] args) {

TcpSocketServerDemo tDemo = new TcpSocketServerDemo(1000)

tDemo.starServer()

}

/**

 * 校验

 * 

 * @param i

 * @return

 */

public int check(int i) {

if (i < ranNumber) {

return -1

} else if (i > ranNumber) {

return 1

}

return 0

}

}

/**

 * 服务端处理线程

 *

 */

class ServerThread implements Runnable {

private TcpSocketServerDemo tDemo

private Socket socket

private DataInputStream dis// 输入流(读取数据)

private DataOutputStream dos// 输出流(发送数据)

public ServerThread(TcpSocketServerDemo tDemo, Socket socket) {

super()

this.tDemo = tDemo

this.socket = socket

try {

dis = new DataInputStream(socket.getInputStream())

dos = new DataOutputStream(socket.getOutputStream())

} catch (IOException e) {

e.printStackTrace()

}

}

public void run() {

String message = ""

int i

int index = 0

int result = 0

while (!message.equals("Quit") && index < 5) {

try {

message = dis.readUTF()

} catch (IOException e) {

e.printStackTrace()

}

try {

i = Integer.parseInt(message)

result = tDemo.check(i)

if (result == 1) {

dos.writeUTF("数字大了")

} else if (result == -1) {

dos.writeUTF("数字小了")

}

if (result == 0) {

dos.writeUTF("猜中了")

break

}

} catch (NumberFormatException | IOException e) {

continue

}

index++

}

try {

dis.close()

dos.close()

socket.close()

} catch (IOException e) {

e.printStackTrace()

}

}

}

TcpSocketClientDemo.java

package yaoshun.InetAddress

import java.io.DataInputStream

import java.io.DataOutputStream

import java.io.IOException

import java.net.Socket

import java.net.UnknownHostException

import java.util.Scanner

/*

 * 演示类:TCP客户端

 */

public class TcpSocketClientDemo {

private Socket socket// 客户端连接(如果建立连接时,本机网络不容或服务器程序未启动则会抛出异常)

private DataInputStream dis// 输入流(读取数据)

private DataOutputStream dos// 输出流(发送数据)

// private String serverIP// 服务器IP地址

// private int serverPort// 服务器监听端口

private String sendData// 发送的数据

public TcpSocketClientDemo(String serverIP, int serverPort) throws UnknownHostException, IOException {

// this.serverIP = serverIP

// this.serverPort = serverPort

socket = new Socket(serverIP, serverPort)

dis = new DataInputStream(socket.getInputStream())

dos = new DataOutputStream(socket.getOutputStream())

sendData = null

startClient()

}

public void startClient() {

try {

// 第一步:创建客户端连接

System.out.println("===我的客户端界面===")

// 第二步:客户端向服务器发送数据

int i = 0

Scanner input = new Scanner(System.in)

while (i < 5) {

System.out.println("请输入你的内容(客户端的):")

sendData = input.nextLine()

if (sendData.equals("quit")) {

break

}

dos.writeUTF(sendData)

sendData = dis.readUTF()

System.out.println("接收到服务器内容:" + sendData)

if (i == 5) {

System.out.println("超过允许次数,请重新连接服务器.")

}

if (sendData.equals("猜中了")) {

i = 5

}

i++

}

input.close()

} catch (Exception e) {

e.printStackTrace()

} finally {

// 第四步:关闭输入流、输出流和客户端连接

try {

dis.close()

dos.close()

socket.close()

} catch (Exception e) {

e.printStackTrace()

}

}

}

public static void main(String[] args) {

try {

new TcpSocketClientDemo("127.0.0.1", 1000)

} catch (IOException e) {

e.printStackTrace()

}

}

}