前面一片学习了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来实现群发!

Socket 多线程编程的更多相关文章

  1. Java socket 多线程编程 示例

    参照网上代码: 1.工程: 2.代码: Client.java package com.my.socket.test; import java.io.BufferedReader; import ja ...

  2. SSM配置Socket多线程编程(RFID签到实例)

    1.SocketServiceLoader.java package cn.xydata.pharmacy.api.app.test; import javax.servlet.ServletCont ...

  3. 多线程编程以及socket编程_Linux程序设计4chapter15

    看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建 ...

  4. Delphi Socket通信及多线程编程总结

    http://cxhblog.blog.sohu.com/41930676.html 一.Socket通信: Delphi在ScktComp单元中对WinSock进行了封装,该单元提供了TAbstra ...

  5. Linux Socket 网络编程

    Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...

  6. 孙鑫VC学习笔记:多线程编程

    孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010  HQU Email:zgzhaobo@gmail.com    QQ:452728574 Latest Modified ...

  7. 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)   介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...

  8. Siege——多线程编程最佳实例

    在英语中,“Siege”意为围攻.包围.同时Siege也是一款使用纯C语言编写的开源WEB压测工具,适合在GNU/Linux上运行,并且具有较强的可移植性.之所以说它是多线程编程的最佳实例,主要原因是 ...

  9. windows socket网络编程基础知识

    下面介绍网络7层协议在WINDOWS的实现: 7层协议 WIN系统 ________________________________________ 7 应用层 7 应用程序 ____________ ...

随机推荐

  1. shiro(安全框架)

    shiro.apache.org JavaSE环境搭建Shiro框架 1/导入与 shiro相关的Jar包 所有集好的环境可以在如下目录查找 复制如上文件到工程中 2/配置文件:储存临时文件 shir ...

  2. 基于redis的 分布式锁 Java实现

    package com.hs.services.lock; import java.util.concurrent.TimeUnit; import javax.annotation.Resource ...

  3. BottomNavigationView 使用

    <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.Cons ...

  4. studio--常见设置

    13.Butterknife插件:zelezny 12.android studio怎么设置打开项目时打开项目列表? 11.stuido   代码背景颜色设置为护眼模式 ======== 13.But ...

  5. UE4 UMG

    转自:https://www.cnblogs.com/kadaj/p/6412937.html 1.创建关卡类 1.创建C++类继承LevelScriptActor 2.打开关卡蓝图 Class Se ...

  6. Letsencrypt SSL免费证书申请(Docker)

    最近需要SSL证书,又不想花钱买,正好看到linux基金会去年底上线了新的开源项目,免费推广SSL遂尝试. Let's Encrypt 介绍 Let’s Encrypt is a free, auto ...

  7. Hive环境的安装

    hive是什么:hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供简单的sql查询功能(HQL) hive有什么用 1.通过类SQL语句快速实现简单的Map ...

  8. Vue 路由及路由默认跳转

    路由就是让根组件动态得去挂载其他组件: 步骤: //路由配置: //.安装 npm install vue-router --save / cnpm install vue-router --save ...

  9. i++ 是线程安全的吗?

    相信很多中高级的 Java 面试者都遇到过这个问题,很多对这个不是很清楚的肯定是一脸蒙逼.内心肯定还在质疑,i++ 居然还有线程安全问题?只能说自己了解的不够多,自己的水平有限. 先来看下面的示例来验 ...

  10. 新部署tomcat,An error occurred at line: [1] index_jsp.java

    环境: centos6.5 32位 oracle jdk 1.8 tomcat 7 问题: yum install tomcat后,返回如下错误: [root@centos]~# curl -v ht ...