Java中的TCP、UDP通讯

TCP

TCP 协议提供面向连接的服务,通过它建立的是可靠地连接。Java 为 TCP 协议提供了两个类:Socke 类和 ServerSocket 类。一个 Socket 实例代表了 TCP 连接的一个客户端,而一个 ServerSocket 实例代表了 TCP 连接的一个服务器端,一般在 TCP Socket 编程中,客户端有多个,而服务器端只有一个,客户端 TCP 向服务器端 TCP 发送连接请求,服务器端的 ServerSocket 实例则监听来自客户端的 TCP 连接请求,并为每个请求创建新的 Socket 实例,由于服务端在调用 accept()等待客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,因此要为每个 Socket 连接开启一个线程。服务器端要同时处理 ServerSocket 实例和 Socket 实例,而客户端只需要使用 Socket 实例。另外,每个 Socket 实例会关联一个 InputStream 和 OutputStream 对象,我们通过将字节写入套接字的 OutputStream 来发送数据,并通过从 InputStream 来接收数据。
From:极客wiki

创建服务器

  1. 通过ServerSocket创建服务套接字,并监听本地端口(以22333端口为例)
//:TCPServer.java

ServerSocket serverSocket = new ServerSocket(22333);
while(true){
	Socket conn = serverSocket.accept();
	onClientArrived(conn);
}
//deal with the request connection
public void abstract onClientArrived(Socket conn);

在新的连接到达前ServerSocket#accept()将处于阻塞状态。当每个连接到达时accept函数都将返回一个Socket实例;
2) 在onClientArrived中处理新连接

@Override
public void onConnArrived(Socket conn) {
	try {
		BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
		String tmpLine = null;
		while ((tmpLine = reader.readLine()) != null){
				System.out.println(tmpLine);
			}	
			reader.close();
			conn.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Reader#readLine()为一个阻塞读取过程,因此需要在客户端使用(启用auto flush)PrintWriterprintln等含有自动newLine的 函数与之配合使用。这里为了简便在读取完成后,直接关闭连接,如果想进行持续会话,则可根据conn的内容来控制时候结束会话,并且需要保证客户端并没有主动关闭连接。另外,通常当ServerSocket#accept()返回新Socket对象后,应该为该对象创建一个任务,并将该任务交由线程池处理。

创建客户端

  1. 客户端套接字
    连接服务22333端口(Dst Port),同时在TCP协议中系统会随机产生一个本地端口(Local Port)供通讯使用。

Socket client = new Socket("127.0.0.1", 22333);
  1. 向套接字中写入数据
//使用PrintWriter向套接字的OutputStream中写入数据
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(client.getOutputStream()), true);
printWriter.println("hello ! this msg is send by  client");
printWriter.close();
client.close();

此处在写入完成直接关闭连接。若希望持续会话,则可在服务端的连接没有关闭的情况下,此处可以通过对Socket输入流的read操作和输出流的write操作实现持续会话
3) 完整代码

//:TCPTest.java
public class TCPTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                TCPServer tcpServer = new TCPServer() {
                    @Override
                    public void onConnArrived(Socket conn) {
                        try {
                            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                            String tmpLine = null;
                            while ((tmpLine = reader.readLine()) != null){
                                System.out.println("receive msg from client" + tmpLine);
                            }
                            reader.close();
                            conn.close();
                            System.out.println("conn shutdown");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                };
                tcpServer.startServer();
            }
        }).start();

        try {
            Socket socket = new Socket("127.0.0.1",TCPServer.PORT);
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
            printWriter.println("hello ! this msg is send by client");
            printWriter.close();
            socket.close();
            System.out.println("client close");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
//:TCPServer.java

public abstract class TCPServer {
    public static final int PORT = 22333;
    private static ServerSocket serverSocket;
    private volatile boolean ifServerIsRunning = false;
    public TCPServer(){
        synchronized (TCPServer.class){
            if (serverSocket == null){
                try {
                    serverSocket = new ServerSocket(PORT);
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public void startServer(){
        if (!ifServerIsRunning){
            ifServerIsRunning = true;
            while (true){
                try {
                    Socket conn = serverSocket.accept();
                    onConnArrived(conn);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public abstract void  onConnArrived(Socket conn);
}

由以上的程序可以看出,Socket就是进程与进程之间的一种通信方式,一个位于服务端,一个位于客户端。Socket并不代表运输层,它只是一个网络编程API,当选用Socket时,代表我们希望使用TCP协议通信,当选用DatagramSocket时代表我们希望使用UDP协议进行通信。

UDP

Java 通过 DatagramPacket 类和 DatagramSocket 类来使用 UDP 套接字,客户端和服务器端都通过DatagramSocket 的 send()方法和 receive()方法来发送和接收数据,用 DatagramPacket 来包装需要发送或者接收到的数据。发送信息时,Java 创建一个包含待发送信息的 DatagramPacket 实例,并将其作为参数传递给DatagramSocket实例的send()方法;接收信息时,Java 程序首先创建一个 DatagramPacket 实例,该实例预先分配了一些空间,并将接收到的信息存放在该空间中,然后把该实例作为参数传递给 DatagramSocket 实例的 receive()方法。在创建 DatagramPacket 实例时,要注意:如果该实例用来包装待接收的数据,则不指定数据来源的远程主机和端口,只需指定一个缓存数据的 byte 数组即可(在调用 receive()方法接收到数据后,源地址和端口等信息会自动包含在 DatagramPacket 实例中),而如果该实例用来包装待发送的数据,则要指定要发送到的目的主机和端口。
From:极客wiki

小结

  • 在读取未关闭的socket的过程中,任何的read()readline()操作都是一个阻塞过程直到读取到内容或者socket关闭,可对比new Scanner(System.in)的类next操作。
  • ServerSocketaccept()操作是同样是一个阻塞过程,直到收到一个连接请求,每次客户端成功发起请求后,accept()都返回一个Socket对象,这个Socket对象将负责与发起请求的本次客户端进行通讯。
  • ReaderreadLine()需要使用'\n'作为此次读取结束标记,否则将一直阻塞在读取状态。
  • “一切皆文件”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值