Socket 多线程编程
前面一片学习了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 多线程编程的更多相关文章
- Java socket 多线程编程 示例
参照网上代码: 1.工程: 2.代码: Client.java package com.my.socket.test; import java.io.BufferedReader; import ja ...
- SSM配置Socket多线程编程(RFID签到实例)
1.SocketServiceLoader.java package cn.xydata.pharmacy.api.app.test; import javax.servlet.ServletCont ...
- 多线程编程以及socket编程_Linux程序设计4chapter15
看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建 ...
- Delphi Socket通信及多线程编程总结
http://cxhblog.blog.sohu.com/41930676.html 一.Socket通信: Delphi在ScktComp单元中对WinSock进行了封装,该单元提供了TAbstra ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- 孙鑫VC学习笔记:多线程编程
孙鑫VC学习笔记:多线程编程 SkySeraph Dec 11st 2010 HQU Email:zgzhaobo@gmail.com QQ:452728574 Latest Modified ...
- 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)
Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥) 介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...
- Siege——多线程编程最佳实例
在英语中,“Siege”意为围攻.包围.同时Siege也是一款使用纯C语言编写的开源WEB压测工具,适合在GNU/Linux上运行,并且具有较强的可移植性.之所以说它是多线程编程的最佳实例,主要原因是 ...
- windows socket网络编程基础知识
下面介绍网络7层协议在WINDOWS的实现: 7层协议 WIN系统 ________________________________________ 7 应用层 7 应用程序 ____________ ...
随机推荐
- ajax 调用webservice 跨域问题
注意两点 1. 在webservice的config中加入这段位置 (注意不是调用webservice的webconfig中加入) <system.webServer> <! ...
- Python shelve 模块
使用json或者pickle持久化数据,能dump多次,但load的话只能取到最新的dump, 因为先前的数据已经被后面dump的数据覆盖掉了. 如果想要实现dump多次不被覆盖,就可以想到使用she ...
- 第0章 概述及常见dos命令
计算机发展史 计算机的发展历史有多长?真正意义上的计算机诞生,距今也只有80多年的时间.80年,对于每一个人来说,是很长的时间,但对于整个历史来说,只是短短的一瞬间. 从第一代电子计算机的发明,到今天 ...
- jlet
项目地址 : https://github.com/kelin-xycs/jlet jlet jlet 一个 javascript 的 小 Lib 本来想写一个 javascript 的 小 Lib ...
- 自己写一个 Hash 表
项目地址: https://github.com/kelin-xycs/HashTableLib 为什么会想要自己写一个 Hash 表, 以前也想过 Hash 表 的 原理, 觉得很神奇, 不过最近 ...
- 2.3 Visio画虚线后插入word或PPT变为实线
选中实线后,左键选择->格式->线条->粗细->自定义->设置为0pt
- Spring生态研习【二】:SpEL(Spring Expression Language)
1. SpEL功能简介 它是spring生态里面的一个功能强大的描述语言,支在在运行期间对象图里面的数据查询和数据操作.语法和标准的EL一样,但是支持一些额外的功能特性,最显著的就是方法调用以及基本字 ...
- js延迟
function sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() + numberMillis; wh ...
- 【mybatis】之trim
<trim prefix="where" prefixOverrides="where" suffixOverrides="and"& ...
- mosquitto centos安装配置
周末弄wordpress的Mysql,一不小心把wordpress弄不好了,写了的好几遍文章也没有了,一怒之下,把整个系统重装了,安装了不带任何软件的新系统,重新搭一遍. 0.安装ftp服务器 #yu ...