使用套接字实现基于 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.tcp2;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. 在构造函数里, 通过参数类型为 InetAddress 类型参数和 3333,初始化了本类
里的 Socket 对象,随后实例化了两类 IO 对象,并通过 start 方法,启动定义在 run 方法内的
本线程的业务逻辑。
2. 在定义线程主体动作的 run 方法里,通过 IO 句柄,向 Socket 信道上传输本客户
端的 ID 号,发送完毕后,传输”byebye”字符串,向服务器端表示本线程的通讯结束。
3. 同样地,catch 从句将处理在 try 语句里遇到的 IO 错误等异常,而在 finally 从句
里,将在通讯结束后关闭客户端的 Socket 句柄。* @author asus**/
class ClientThreadCode extends Thread {// 客户端的 socketprivate Socket socket;// 线程统计数,用来给线程编号private static int cnt = 0;private int clientId = cnt++;private BufferedReader in;private PrintWriter out;// 构造函数public ClientThreadCode(InetAddress addr) {try {socket = new Socket(addr, 3333);} catch (IOException e) {e.printStackTrace();}// 实例化 IO 对象try {in = new BufferedReader(new InputStreamReader(socket.getInputStream()));out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);// 开启线程start();} catch (IOException e) {// 出现异常,关闭 sockettry {socket.close();} catch (IOException e2) {e2.printStackTrace();}}}// 线程主体方法public void run() {try {out.println("Hello Server,My id is " + clientId);String str = in.readLine();System.out.println(str);out.println("byebye");} catch (IOException e) {e.printStackTrace();} finally {try {socket.close();} catch (IOException e) {e.printStackTrace();}}}
}
package my.socket.tcp2;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.Socket;
/**
这个类的业务逻辑说明如下:
1. 这个类通过继承 Thread 类来实现线程的功能,也就是说,在其中的 run 方法里,定义了该线程启动后要执行的业务动作。
2. 这个类提供了两种类型的重载函数。在参数类型为 Socket 的构造函数里, 通过参数,初始化了本类里的 Socket 对象,同时实例化了两类 IO 对象。在此基础上,通过 start方法,启动定义在 run 方法内的本线程的业务逻辑。
3. 在定义线程主体动作的 run 方法里,通过一个 for(;;)类型的循环,根据 IO 句柄,读取从 Socket 信道上传输过来的客户端发送的通讯信息。如果得到的信息为“byebye”,则表明本次通讯结束,退出 for 循环。
4. catch 从句将处理在 try 语句里遇到的 IO 错误等异常,而在 finally 从句里,将在通讯结束后关闭客户端的 Socket 句柄。上述的线程主体代码将会在 ThreadServer 类里被调用。* @author asus**/
public class ServerThreadCode extends Thread {// 客户端的 socketprivate Socket clientSocket;// IO 句柄private BufferedReader sin;private PrintWriter sout;// 默认的构造函数public ServerThreadCode() {}public ServerThreadCode(Socket s) throws IOException {clientSocket = s;// 初始化 sin 和 sout 的句柄sin = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));sout = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())), true);// 开启线程start();}// 线程执行的主体函数public void run() {try {// 用循环来监听通讯内容for (;;) {String str = sin.readLine();// 如果接收到的是 byebye,退出本次通讯if (str.equals("byebye")) {break;}System.out.println("In Server reveived the info: " + str);sout.println(str);}System.out.println("closing the server socket!");} catch (IOException e) {e.printStackTrace();} finally {System.out.println("close the Server socket and the io.");try {clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}
}
package my.socket.tcp2;import java.io.IOException;
import java.net.InetAddress;
/*** 这段代码执行以后,在客户端将会有 3 个通讯线程,每个线程首先将先向服务器端发送"Hello
Server,My id is "的字符串,然后发送”byebye”,终止该线程的通讯。* @author asus**/
public class ThreadClient {public static void main(String[] args) throws IOException, InterruptedException {int threadNo = 0;InetAddress addr = InetAddress.getByName("localhost");for (threadNo = 0; threadNo < 3; threadNo++) {new ClientThreadCode(addr);}}
}
package my.socket.tcp2;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
这段代码的主要业务逻辑说明如下:
1. 首先定义了通讯所用的端口号,为 3333。
2. 在 main 函数里,根据端口号,创建一个 ServerSocket 类型的服务器端的 Socket,
用来同客户端通讯。
3. 在 for(;;)的循环里,调用 accept 方法,监听从客户端请求过来的 socket,请注意
这里又是一个阻塞。当客户端有请求过来时,将通过 ServerThreadCode 的构造函数,创建一
个线程类,用来接收客户端发送来的字符串。在这里我们可以再一次观察 ServerThreadCode
类,在其中,这个类通过构造函数里的 start 方法,开启 run 方法,而在 run 方法里,是通过
sin 对象来接收字符串,通过 sout 对象来输出。
4. 在 finally 从句里,关闭服务器端的 Socket,从而结束本次通讯。* @author asus**/
public class ThreadServer {// 端口号static final int portNo = 3333;public static void main(String[] args) throws IOException {// 服务器端的 socketServerSocket s = new ServerSocket(portNo);System.out.println("The Server is start: " + s);try {for (;;) {// 阻塞,直到有客户端连接Socket socket = s.accept();// 通过构造函数,启动线程new ServerThreadCode(socket);}} finally {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 编程相关推荐

  1. java web接收tcp_Java多线程实现TCP网络Socket编程(C/S通信)

    开篇必知必会 在前一篇<基于TCP协议网络socket编程(java实现C/S通信)>,实际存在一个问题,如果服务器端在建立连接后发送多条信息给客户端,客户端是无法全部接收的,原因在于客户 ...

  2. java基础 day12-FileInputStream类,文件的复制,缓冲流,Propertes文件,xml文件读写,网络socket编程(构建TCP客户端),内部类

    FileInputStream类的其他常用方法() /**在project下新建temp文件,内容为abcdef*FileInputStream类的其他常用方法:* int available():返 ...

  3. JAVA网络IO编程

    2019独角兽企业重金招聘Python工程师标准>>> JAVA网络IO编程(BIO NIO AIO) 一.传统的BIO编程 1.网络编程的基本模型是C/S模型,即两个进程间的通信. ...

  4. api有哪些 javasocket_基于java的socket编程及API解析

    一.socket通讯过程 1.socket与socket编程简介: socket 被翻译为"套接字",它是计算机之间进行通信的一种约定或一种方式.通过 socket 这种约定,一台 ...

  5. 网络socket编程指南 (转)

    Beej网络socket编程指南 -------------------------------------------------------------------------------- 介绍 ...

  6. 【java】网络socket编程简单示例

    1 package 网络编程; 2 3 import java.io.IOException; 4 import java.io.PrintStream; 5 import java.net.Serv ...

  7. Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)

    本文会从传统的BIO到NIO再到AIO自浅至深介绍,并附上完整的代码讲解. 下面代码中会使用这样一个例子:客户端发送一段算式的字符串到服务器,服务器计算后返回结果到客户端. 代码的所有说明,都直接作为 ...

  8. Java的Socket编程

    socket编程一般指的就是网络编程,常见的服务端和客户机都是必不可少的,今天小千就来给大家介绍一下socket编程中常见的概念问题. 一.常见传输协议 1.tcp协议TCP (Transmissio ...

  9. (转载)Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)

    转载请注明出处:http://blog.csdn.net/anxpp/article/details/51512200,谢谢! 本文会从传统的BIO到NIO再到AIO自浅至深介绍,并附上完整的代码讲解 ...

最新文章

  1. breakContinue标签使用
  2. 读《大道至简-- 第二章 是懒人造就了方法》 有感
  3. reactjs redux chrome扩展插件
  4. php swoole 内存,swoole 占用内存到10M 报错
  5. Why I could not put extension fields done on CUSTOMER_H to WebUI
  6. SPRING IN ACTION 第4版笔记-第二章-004-Bean是否单例
  7. 在线文本转2-36任意进制工具
  8. 公司只有1个测试,领导却让我同时操作1000个手机号
  9. 二、十进制数字快速转换为16进制字符
  10. 汽车质量管理体系IATF 16949和培训及相关的标准(主要是电动汽车)
  11. python修改wav文件声音大小_如何用python批量调整视频声音
  12. GhostNet论文
  13. 如何构建自己的Java学习体系?
  14. Ubuntu10.04下搞定D-Link DWA-125无线网卡驱动
  15. 金蝶K3案例教程简介
  16. 做网站推广最实用的88种网站推广方法
  17. AssertionError: Invalid device id
  18. 东秦OJ_1841: 超级密码
  19. 集算器读写 json
  20. 信号与系统(二十一)——无失真传输和理想低通滤波器

热门文章

  1. 许昌电气学校电话计算机,许昌电气职业学院
  2. mysql cascaded local_学习笔记-mysql_视图
  3. telegram电脑一直显示连接中_小事不求人!学会这个方法,一个电脑连接多个显示器不再是问题...
  4. UAC bypass 理论学习
  5. 代码签名证书Authenticode签名伪造——PE文件的签名伪造与签名验证劫持
  6. linux目录和文件管理命令
  7. 3399引擎_RK3399开发板 AIO-3399C六核高性能主板
  8. 步步高vivo高通解锁工具_高通人工智能开放日,窥见5G+AI的未来
  9. 南宁师范大学计算机与信息工程学院研究生,南宁师范大学计算机与信息工程学院(专业学位)职业技术教育保研...
  10. python函数调用位置_python函数定义,调用,传参,位置参数及关键字参数,返回值