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 应用程序 ____________ ...
随机推荐
- python, generator.next()和send()
对于普通的生成器,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(第4行)后,跳出生成器函数. 然后第二个next调用,进入生成器函数后,从y ...
- Java高级特性 第2节 java中常用的实用类(1)
一.Java API Java API即Java应用程序编程接口,他是运行库的集合,预先定义了一些接口和类,程序员可以直接调用:此外也特指API的说明文档,也称帮助文档. Java中常用的包: jav ...
- django中多个app放入同一文件夹apps
开发IDE:pycharm 新建一个apps文件夹 需要整理的app文件夹拖到同一个文件夹中,即apps.(弹出对话框,取消勾选Search for references) 在pycharm 中,右键 ...
- MySQL通过分组计算百分比
公司在做柯米克的分析报告,需要我这边把汽车之家柯米克论坛的评论数据和评论用户所在地的数据获取,通过爬虫的方式很快的解决了数据的问题,但是需要我提取下各省评论人数的比例,所以在数据库里面直接计算了相关的 ...
- 枚举、反射等 GetEnumName GetEnumDescription
/// <summary> /// Retrieves the name of the constant in the specified enumeration that has the ...
- git提交代码五部曲
From: https://jingyan.baidu.com/article/359911f5a4fe4b57fe03060d.html 正常使用git时,提交代码五部曲. 工具/原料 电脑 已 ...
- 8、sort排序中比较函数的几种应用方式
1.待排序中的元素作数组的下标或map的键值 例题:PAT甲级_1141 PAT Ranking of Institutions #include<bits/stdc++.h> using ...
- [UE4]Spacer
一.Spacer:留白占位控件 二.如下图所示,如果想要2个按钮都在容器右对齐: 三.可以放一个Spacer到最左边,设置成Fill,Spacer控件就是起到占位的作用.
- sqlserver 带输出参数的存储过程的创建与执行
创建 use StudentManager go if exists(select * from sysobjects where name='usp_ScoreQuery4') drop proce ...
- 论气机之"左升右降"
生命现象源于气机的出入升降运动. “出入废则神机化灭,升降息则气立孤危.故非出入,则无以生长壮老已:非升降,则无以生长化收藏”(<素问·六微旨大论>),升降是气机主要的运动形式之一,是 ...