Java 网络 socket 编程
使用套接字实现基于 TCP 协议的服务器和客户机程序
依据 TCP 协议,在 C/S 架构的通讯过程中,客户端和服务器的 Socket 动作如下:
客户端:
1.用服务器的 IP 地址和端口号实例化 Socket 对象。
2.调用 connect 方法,连接到服务器上。
3.将发送到服务器的 IO 流填充到 IO 对象里,比如 BufferedReader/PrintWriter。
4.利用 Socket 提供的 getInputStream 和 getOutputStream 方法,通过 IO 流对象,向服务器发送数据流。
5. 通讯完成后,关闭打开的 IO 对象和 Socket。
服务器:
1. 在服务器,用一个端口来实例化一个 ServerSocket 对象。此时,服务器就可以这个端口时刻监听从客户端发来的连接请求。
2.调用 ServerSocket 的 accept 方法,开始监听连接从端口上发来的连接请求。
3.利用 accept 方法返回的客户端的 Socket 对象,进行读写 IO 的操作通讯完成后,关闭打开的流和 Socket 对象。
下面是一个简单的客户端与服务器端的例子:
客户端:
package my.socket.tcp;import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; /** 上述客户端代码的主要业务逻辑是: 1. 同样定义了通讯端口号,这里给出的端口号必须要和服务器端的一致。 2. 在 main 函数里,根据地址信息“localhost”,创建一个 InetAddress 类型的对象addr。这里,因为我们把客户端和服务器端的代码都放在本机运行,所以同样可以用“127.0.0.1”字符串,来创建 InetAddress 对象。 3. 根据 addr 和端口号信息,创建一个 Socket 类型对象,该对象用来同服务器端的ServerSocket 类型对象交互,共同完成 C/S 通讯流程。 4. 同样地创建 in 和 out 两类 IO 句柄,用来向服务器端发送和接收数据流。 5. 通过 out 对象,向服务器端发送"Hello Server,I am …"的字符串。发送后,同样可以用 in 句柄,接收从服务器端的消息。 6. 利用 out 对象,发送”byebye”字符串,用以告之服务器端,本次通讯结束。 7. 在 finally 从句里,关闭 Socket 对象,断开同服务器端的连接。* @author asus**/ public class ClientCode {static String clientName = "Mike";// 端口号public static int portNo = 3333;public static void main(String[] args) throws IOException {// 设置连接地址类,连接本地InetAddress addr = InetAddress.getByName("localhost");// 要对应服务器端的 3333 端口号Socket socket = new Socket(addr, portNo);try {System.out.println("socket = " + socket);// 设置 IO 句柄BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);out.println("Hello Server,I am " + clientName);String str = in.readLine();System.out.println(str);out.println("byebye");} finally {System.out.println("close the Client socket and the io.");socket.close();}} }服务器端:
package my.socket.tcp;import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /*** 编写服务器端的主体代码:这段代码的主要业务逻辑是: 1. 在上述代码里的 main 函数前,我们设置了通讯所用到的端口号,为 3333。 2. 在 main 函数里,根据给定 3333 端口号,初始化一个 ServerSocket 对象 s,该对象用来承担服务器端监听连接和提供通讯服务的功能。 3. 调用 ServerSocket 对象的 accept 方法,监听从客户端的连接请求。当完成调用accept 方法后,整段服务器端代码将回阻塞在这里,直到客户端发来 connect 请求。 4. 当客户端发来 connect 请求,或是通过构造函数直接把客户端的 Socket 对象连接到服务器端后,阻塞于此的代码将会继续运行。此时服务器端将会根据 accept 方法的执行结果,用一个 Socket 对象来描述客户端的连接句柄。 5. 创建两个名为 in 和 out 的对象,用来传输和接收通讯时的数据流。 6. 创建一个 while(true)的死循环,在这个循环里,通过 in.readLine()方法,读取从客户端发送来的 IO 流(字符串),并打印出来。如果读到的字符串是“byebye”,那么退出while 循环。 7. 在 try…catch…finally 语句段里,不论在 try 语句段里是否发生异常,并且不论这些异常的种类,finally 从句都将会被执行到。在 finally 从句里,将关闭描述客户端的连接句柄 socket 对象和 ServerSocket 类型的 s 对象。* @author asus**/ public class ServerCode {// 设置端口号public static int portNo = 3333;public static void main(String[] args) throws IOException {ServerSocket s = new ServerSocket(portNo);System.out.println("The Server is start: " + s);// 阻塞,直到有客户端连接Socket socket = s.accept();try {System.out.println("Accept the Client: " + socket);// 设置 IO 句柄BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);while (true) {String str = in.readLine();if (str.equals("byebye")) {break;}System.out.println("In Server reveived the info: " + str);out.println(str);}} finally {System.out.println("close the Server socket and the io.");socket.close();s.close();}} }先运行服务器端,再运行客户端之后,可以看到服务器端接收到来自客户端发送的信息。
通常网络编程都是用多线程来实现,将大大地提高服务器端的利用效率,并能使服务器端能具备完善的
服务功能。
首先运行服务器端,再运行客户端,可以清楚的看到服务器端多线程接收到来自客户端的消息。
下面是同时开启服务器端和客户端,两者进行不间断的通信。
package my.socket.udp;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException;public class ClientBean {// 描述 UDP 通讯的 DatagramSocket 对象private DatagramSocket ds;// 用来封装通讯字符串private byte buffer[];// 客户端的端口号private int clientport;// 服务器端的端口号private int serverport;// 通讯内容private String content;// 描述通讯地址private InetAddress ia;public ClientBean() throws SocketException, UnknownHostException {buffer = new byte[1024];clientport = 1985;serverport = 1986;content = "";ds = new DatagramSocket(clientport);ia = InetAddress.getByName("localhost");}public void sendToServer() throws IOException {buffer = content.getBytes();ds.send(new DatagramPacket(buffer, content.length(), ia, serverport));}// 以下是各属性的 Get 和 Set 类型方法public byte[] getBuffer() {return buffer;}public void setBuffer(byte[] buffer) {this.buffer = buffer;}public int getClientport() {return clientport;}public void setClientport(int clientport) {this.clientport = clientport;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public DatagramSocket getDs() {return ds;}public void setDs(DatagramSocket ds) {this.ds = ds;}public InetAddress getIa() {return ia;}public void setIa(InetAddress ia) {this.ia = ia;}public int getServerport() {return serverport;}public void setServerport(int serverport) {this.serverport = serverport;} } package my.socket.udp;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException;public class ServerBean {// 描述 UDP 通讯的 DatagramSocket 对象private DatagramSocket ds;// 用来封装通讯字符串private byte buffer[];// 客户端的端口号private int clientport;// 服务器端的端口号private int serverport;// 通讯内容private String content;// 描述通讯地址private InetAddress ia;public ServerBean() throws SocketException, UnknownHostException {buffer = new byte[1024];clientport = 1985;serverport = 1986;content = "";ds = new DatagramSocket(serverport);ia = InetAddress.getByName("localhost");}public void listenClient() throws IOException {// 在循环体里接收消息while (true) {// 初始化 DatagramPacket 类型的变量DatagramPacket dp = new DatagramPacket(buffer, buffer.length);// 接收消息,并把消息通过 dp 参数返回ds.receive(dp);content = new String(dp.getData(), 0, dp.getLength());// 打印消息print();}}public void print() {System.out.println(content);}public DatagramSocket getDs() {return ds;}public void setDs(DatagramSocket ds) {this.ds = ds;}public byte[] getBuffer() {return buffer;}public void setBuffer(byte[] buffer) {this.buffer = buffer;}public int getClientport() {return clientport;}public void setClientport(int clientport) {this.clientport = clientport;}public int getServerport() {return serverport;}public void setServerport(int serverport) {this.serverport = serverport;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public InetAddress getIa() {return ia;}public void setIa(InetAddress ia) {this.ia = ia;}} package my.socket.udp;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;public class UDPClient implements Runnable {public static String content;public static ClientBean client;public void run() {try {client.setContent(content);client.sendToServer();} catch (Exception ex) {System.err.println(ex.getMessage());}}// end of run// main 方法// …public static void main(String args[]) throws IOException {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));client = new ClientBean();System.out.println("客户端启动...");while (true) {// 接收用户输入content = br.readLine();// 如果是 end 或空,退出循环if (content == null || content.equalsIgnoreCase("end") || content.equalsIgnoreCase("")) {break;}// 开启新线程,发送消息new Thread(new UDPClient()).start();}} } package my.socket.udp;import java.io.IOException;public class UDPServer {public static void main(String args[]) throws IOException {System.out.println("服务器端启动...");// 初始化 ServerBean 对象ServerBean server = new ServerBean();// 开启监听程序server.listenClient();} }先运行服务器端,再运行客户端。
在客户端输入想要发送的字符,在服务器端可以接收到。
总结
以上是生活随笔为你收集整理的Java 网络 socket 编程的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Eclipse(javaweb)刚换工作
- 下一篇: Java反射,从0开始