前面一片学习了TCP/IP的基础网络编程,并给出了简单的服务端与客户端通信交互的例子。还介绍了UPC的通信例子。

这次学习TCP/IP的多线程编程。因为涉及到TCP/IP一般都是多线程,服务端会一直监听端口,多个客户端发来信息,收到某个客户端发来的数据后,如果所有处理都放在服务端,这样程序就会显得很臃肿。就需要单独启动一个线程去执行,来一个客户端就启动一个对应的程序。

下面给出具体例子:Java 多线程实现Socket通信

 package lesson1220Socket;

 import java.net.ServerSocket;
import java.net.Socket; public class SeverTCPDemo { private static ServerSocket ss; //定义成static类型是有原因的,因为ss不能进行close. public static void main(String[] args) throws Exception {
ss = new ServerSocket(9999);
Socket socket = null;
int num = 0;
System.out.println("******服务器已启动,等待客户端连接*****"); while(true){
socket = ss.accept();
num++;
new SocketThread(socket,num).start();
}
}
} package lesson1220Socket; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket; public class SocketThread extends Thread { Socket socket;
private int num; public SocketThread(Socket socket, int num) {
super();
this.socket = socket;
this.num = num;
} /* (non-Javadoc)
* @see java.lang.Thread#run()
*/
@Override
public void run() { InputStream is = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine())!=null){
System.out.println("客户端发来消息:" + info);
}
socket.shutdownInput(); os = socket.getOutputStream();
pw= new PrintWriter(os);
pw.write("hello, I am Server! you are " + num + " Client!");
pw.flush();
socket.shutdownOutput(); } catch (IOException e) {
e.printStackTrace();
}finally{
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
pw.close();
}
}
} package lesson1220Socket; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException; public class ClientTCPDemo { public static void main(String[] args){ Socket socket = null;
OutputStream os = null;
PrintWriter pw = null;
InputStream is = null;
BufferedReader br = null; try {
socket = new Socket("127.0.0.1", 9999);
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("hello, server! I am client!");
pw.flush(); socket.shutdownOutput(); is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine())!=null){
System.out.println("服务端返回信息:" + info);
} socket.shutdownInput(); } catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
os.close();
} catch (IOException e1) {
e1.printStackTrace();
}
pw.close();
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} }

运行结果:分别启动三个客户端:

******服务器已启动,等待客户端连接*****
客户端发来消息:hello, server! I am client!
客户端发来消息:hello, server! I am client!
客户端发来消息:hello, server! I am client!

客户端:

服务端返回信息:hello, I am Server! you are 1 Client!

服务端返回信息:hello, I am Server! you are 2 Client!

服务端返回信息:hello, I am Server! you are 3 Client!

Note:一定要等程序都运行完后,才可以关闭相关资源例如:os,is,pw等。

上面的例子通信一次就关闭了,下面再给出几个TCP/IP通信例子,两边可以交互通信:

例子1:两边通信,按顺序一发一收。客户端和服务器都有收发功能,并且收发有相应的顺序,都对应相应的阻塞:缓冲区br.readLine(), 键盘输入sc.nextLine()。

 package lesson1220Socket;

 import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner; public class ChatServer {
public static ServerSocket ss; public ChatServer() {
try {
ss = new ServerSocket(8888);
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){
Socket socket = null;
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null; try {
System.out.println("服务端启动。。。。。。");
socket = ss.accept(); is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr); os = socket.getOutputStream();
//pw = new PrintWriter(os); //很奇怪,这个地方要这么写,要不然数据发不出去?
OutputStreamWriter osw = new OutputStreamWriter(os);
pw = new PrintWriter(osw, true);//解释上一行,要输入参数true,会自动刷新flush() Scanner sc = new Scanner(System.in);
//也可以通过下面code来获取系统输入
//BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in));
//br2.readLine(); String info; while(true){ System.out.println("收到客户端消息: " + br.readLine());
pw.println(info = sc.nextLine());
//pw.println(info = br2.readLine());
if("bye".equals(info)){
System.out.println("byebye!");
break;
}
} } catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
new ChatServer().start();
}
} package lesson1220Socket; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner; public class ChatClient { Socket socket ; public ChatClient() {
try {
this.socket = new Socket("127.0.0.1", 8888);
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){ InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
os = socket.getOutputStream();
pw = new PrintWriter(os,true); //true代表 每写一行自动刷新 Scanner sc = new Scanner(System.in);
//BufferedReader br2 = new BufferedReader(new InputStreamReader(System.in));
//br2.readLine(); String info;
while(true){
pw.println(info = sc.nextLine());
//pw.write(info = sc.nextLine()+"\r\n");
//pw.flush();
//详细讲解可以看帖子:https://www.cnblogs.com/wxgblogs/p/5347996.html
System.out.println("收到服务端回应消息:" + br.readLine()); if("bye".equals(info)){
System.out.println("byebye!");
break;
}
} } catch (IOException e) {
e.printStackTrace();
} } public static void main(String[] args) {
new ChatClient().start();
} }

必须按顺序进行收发,不能同时进行。

要想收发同时进行,收发就不能再同一个线程内,就应该有两个线程,一个负责发,一个负责收。

例子2:通过将收发单独分开,用线程实现。两边可以任意发送。

 package lesson1220Socket;

 import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket; public class ChatServer2 { public static ServerSocket ss;
private Socket socket; public ChatServer2() {
try {
ss = new ServerSocket(8888);
socket = ss.accept();
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){ Thread st = new Thread(new SendThread(socket));
st.start();
Thread rv = new Thread(new ReceiveThread(socket));
rv.start(); } public static void main(String[] args) {
new ChatServer2().start();
} } package lesson1220Socket; import java.io.IOException;
import java.net.Socket; public class ChatClient2 {
Socket socket ; public ChatClient2() {
try {
this.socket = new Socket("127.0.0.1", 8888);
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){ Thread st = new Thread(new SendThread(socket));
st.start();
Thread rv = new Thread(new ReceiveThread(socket));
rv.start(); } public static void main(String[] args) {
new ChatClient2().start();
} } package lesson1220Socket; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner; public class SendThread implements Runnable { private Socket socket;
public SendThread(Socket socket) {
this.socket = socket;
} @Override
public void run() { OutputStream os = null;
PrintWriter pw = null; try {
os = socket.getOutputStream();
pw = new PrintWriter(os, true);
Scanner sc = new Scanner(System.in); while(true){
pw.println(sc.nextLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
} package lesson1220Socket; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket; public class ReceiveThread implements Runnable { Socket socket;
public ReceiveThread(Socket socket) {
this.socket = socket;
} @Override
public void run() { InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null; try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr); while(true){
System.out.println("收到消息:" + br.readLine());
} } catch (IOException e) {
e.printStackTrace();
}
}
}

服务端和客户端共用收发线程,只要两边Socket连接建立完成,所有的工作都是在收发线程完成。

下面考虑多发通信问题。

例子3:一对多通信。 多个客户端同时给服务端发消息,然后服务端回消息,这个回的是群发消息。

 package lesson1220Socket;

 import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Scanner; public class ChatServer3 { public static ServerSocket ss;
Socket cs;
ArrayList<Socket> list = new ArrayList<Socket>();
private boolean isGroup = true;
private Scanner input = new Scanner(System.in); public ChatServer3() { try {
ss = new ServerSocket(7777);
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){
System.out.println("服务端启动。。。");
while(true){
try {
cs = ss.accept();
list.add(cs);
System.out.println("新的连接" + cs.getPort()); Thread receiveThread = new Thread(new ReceiveThread(cs));
receiveThread.start(); if(isGroup){
isGroup = false;
new SendFunc().start();
} } catch (IOException e) {
e.printStackTrace();
}
}
} public class SendFunc extends Thread {
@Override
public void run() {
OutputStream os = null;
PrintWriter pw = null;
while(true){
try {
String msg =input.nextLine();
for(Socket sc:list){
os = sc.getOutputStream();
pw = new PrintWriter(os, true);
pw.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} public static void main(String[] args) {
new ChatServer3().start();
} } package lesson1220Socket; import java.io.IOException;
import java.net.Socket; public class ChatClient3 {
Socket socket; public ChatClient3() {
try {
System.out.println("客户端启动。。。。。");
socket = new Socket("127.0.0.1", 7777);
} catch (IOException e) {
e.printStackTrace();
}
} public void start(){
Thread sendThread = new Thread(new SendThread(socket));
sendThread.start(); Thread receiveThread = new Thread(new ReceiveThread(socket));
receiveThread.start();
} public static void main(String[] args) {
new ChatClient3().start();
} } package lesson1220Socket; import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner; public class SendThread implements Runnable { private Socket socket;
public SendThread(Socket socket) {
this.socket = socket;
} @Override
public void run() { OutputStream os = null;
PrintWriter pw = null; try {
os = socket.getOutputStream();
pw = new PrintWriter(os, true);
Scanner sc = new Scanner(System.in); while(true){
pw.println(sc.nextLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
} package lesson1220Socket; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket; public class ReceiveThread implements Runnable { Socket socket;
public ReceiveThread(Socket socket) {
this.socket = socket;
} @Override
public void run() { InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null; try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr); while(true){
System.out.println("收到消息:" + br.readLine());
} } catch (IOException e) {
e.printStackTrace();
}
}
}

上面code可以实现多个客户端向服务端发送消息,服务端可以群发消息。客户端没有做任何改变。

只需修改服务端,就是如何让消息群发出去,由一个线程来控制,循环遍历所有socket,然后将消息发出去。

网上很多帖子都是服务端自动回一个固定消息,实际上没有实现一对多通信。

但是上面的功能不能实现服务端向指定客户端回消息。若是收到一个客户端,服务端就启动一个收发线程,收的线程没有问题,可是发的线程有问题。 没法进行维护?也不知道该数据发向哪个Socket????

下面这两个也是很好的通过线程来调用Socket的例子:

https://www.cnblogs.com/sddychj/p/6102192.html

https://www.cnblogs.com/qqzy168/p/3772215.html

https://blog.csdn.net/sl_40/article/details/53232423------这个博客解决了我群发消息的问题。

https://blog.csdn.net/aiynmimi/article/details/47323165------这个例子也实现多个客户端向服务端发消息,服务端群发消息。这个里面有界面,通过ActionListener来实现群发!

最新文章

  1. 闲话Promise机制
  2. VXLAN 概念(Part II)- 每天5分钟玩转 OpenStack(109)
  3. js实现四舍六入 奇进偶舍
  4. Javascript模块化规范
  5. HTTPS协议说明
  6. RTL2832U+R820电视棒跟踪飞机轨迹教程(ADS-B)
  7. svn服务端配置
  8. JavaScript学习总结【2】、JS基础
  9. 工作总结:MFC自写排序算法(升序)
  10. TXT四则运算计算器
  11. Autolayout 第三方开源库
  12. 企业信息化快速开发平台--JeeSite
  13. vue开发常用配置
  14. 项目笔记:2018年4月(SpringCloud架构和SpringBoot框架)
  15. delphi 的 ORM 框架
  16. java枚举类型
  17. MAC shell ps 命令详解(转)
  18. JOffice中的权限管理--功能粒度的权限管理配置
  19. Django popup示例
  20. HDU 2095 find your present (2) 动态链表

热门文章

  1. HTTPS协议加密原理解析
  2. css 样式控制文本过长实现省略号
  3. Python递归解压缩多级.zip压缩包
  4. linux用ssh登录卡或者慢
  5. oracle12建立非C##用户并且导入数据
  6. 使用docker查看jvm状态,在docker中使用jmap,jstat
  7. 安卓打开远程调试(免root)
  8. 一个java使用redis的简单案例
  9. js数组条件筛选——map()
  10. Servlet工作原理解析