java 网络编程Socket
TCP:
通过TCP协议传输,得到的是一个顺序的无差错的数据流。
发送方和接收方的成对的两个socket之间必须建立连接,
以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,
另一个socket可以要求进行连接,一旦这两个socket连接起来,
它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。
1.建立服务器端连接(MyServer):
ServerSocket serverSocket = new ServerSocket(6666);//设置端口号6666
2.等待传输:
Socket socket = serverSocket.accept();
3.建立客户端(MyCilent):
Socket socket=new Socket("127.0.0.1",6666);//127.0.0.1本机地址
4.编写输入输出流:
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
System.out.println("服务端等待消息");
while (true) {
Msg msg = (Msg) ois.readObject();
System.out.println("服务端收到消息:" + msg); for (Socket s : MyService.clients) {
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
oos.writeObject(msg);
oos.flush();// 刷新就会发送
}
所有源码:
客户端 MyCilent:
package com.etc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner; public class MyCilent {
public static void main(String[] args) throws UnknownHostException, IOException {
//设置服务器IP和端口号
Socket socket=new Socket("127.0.0.1",6666);
System.out.println("客户端已连接"); //开启客户端发送消息线程
new Thread(new ClientThread(socket)).start(); //输出流
ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream()); System.out.println("客户端准备发送消息");
Scanner sc=new Scanner(System.in); System.out.println("请输入用户名:");
String name= sc.next();
//无限循环输出
while(true) {
System.out.println("请输入你传的消息");
String i=sc.nextLine();
//传入msg对象
Msg msg=new Msg(name,i);
//写入数据
oos.writeObject(i);
//刷新
oos.flush();
System.out.println(name+":"+i);
}
}
}
class ClientThread implements Runnable { private Socket socket; public ClientThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
while (true) {
try {
//输入流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
//将输入的信息传入msg对象
Msg msg = (Msg) ois.readObject();
System.out.println("服务端响应:" + msg);
} catch (Exception e) {
e.printStackTrace();
}
}
} }
服务端 MyService:
package com.etc; import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List; public class MyService {
public static List<Socket> clients=new ArrayList<>(); public static void main(String[] args) throws UnknownHostException, IOException {
//建立服务器端口号
ServerSocket serverSocket = new ServerSocket(6666);
System.out.println("服务器启动,监听6666端口");
while (true) {
//等待消息传入
Socket socket = serverSocket.accept();
//将客户端添加到服务器
clients.add(socket);
// 获取对方ip
InetAddress inetAddress = socket.getInetAddress();
System.out.println("服务器等待" + inetAddress + ":"+socket);
//开启服务器输入输出线程
new Thread(new ServerThread(socket)).start();
} } } class ServerThread implements Runnable { private Socket socket; public ServerThread(Socket socket) {
this.socket = socket;
} @Override
public void run() {
try {
//输入流
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
System.out.println("服务端等待消息");
while (true) {
//将输入信息添加到msg对象中
Msg msg = (Msg) ois.readObject();
System.out.println("服务端收到消息:" + msg); for (Socket s : MyService.clients) {
//输出客户端发送的消息
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
//写入消息
oos.writeObject(msg);
oos.flush();// 刷新就会发送
} } } catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
} }
消息对象:
package com.etc; import java.io.Serializable; public class Msg implements Serializable { private String name;
private String body; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getBody() {
return body;
} public void setBody(String body) {
this.body = body;
} public Msg(String name, String body) {
super();
this.name = name;
this.body = body;
} public Msg() {
super();
} @Override
public String toString() {
return "Msg [name=" + name + ", body=" + body + "]";
} }
1.客户端:
① 创建Socket对象,指明需要连接的服务器的地址和端口号
② 连接建立后,通过输出流想服务器端发送请求信息
③ 通过输入流获取服务器响应的信息
④ 关闭响应资源
2.应用多线程实现服务器与多客户端之间的通信
① 服务器端创建ServerSocket,循环调用accept()等待客户端连接
② 客户端创建一个socket并请求和服务器端连接
③ 服务器端接受苦读段请求,创建socket与该客户建立专线连接
④ 建立连接的两个socket在一个单独的线程上对话
⑤ 服务器端继续等待新的连接
小小测试:如有需求下面附一个更好的完整版
package com.etc; 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.Socket;
import java.util.Scanner; /**
* 控制台聊天程序 客户端应用程序
*
*
*/
public class ChatClient { // 客户端用于与服务端连接的Socket
private Socket clientSocket; /**
* 构造方法,客户端初始化
*/
public ChatClient() {
try {
/*
* socket(String host, int port) 地址: IP地址,用来定位网络上的计算机 端口: 用来找到远端计算机上用来连接的服务端应用程序
*/
clientSocket = new Socket("localhost", 12580);
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 客户端昵称验证方法
*
* @param 为Scanner
*/
private void inputNickName(Scanner scan) throws Exception {
String nickName = null;
// 创建输出流
PrintWriter pw = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream(), "UTF-8"), true);
// 创建输入流
BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), "UTF-8"));
while (true) {
System.out.println("请创建您的昵称:");
nickName = scan.nextLine();
if (nickName.trim().equals("")) {
System.out.println("昵称不得为空");
} else {
pw.println(nickName);
String pass = br.readLine();
if (pass != null && !pass.equals("OK")) {
System.out.println("昵称已经被占用,请更换!");
} else {
System.out.println("你好!" + nickName + "可以开始聊天了");
break;
}
}
}
} /*
* 客户端启动的方法
*/
public void start() {
try {
/*
* 创建Scanner,读取用户输入内容 目的是设置客户端的昵称
*/
Scanner scanner = new Scanner(System.in);
inputNickName(scanner);
/*
* 将用于接收服务器端发送过来的信息的线程启动
*/
Runnable run = new GetServerMsgHandler();
Thread t = new Thread(run);
t.start();
/*
* 建立输出流,给服务端发信息
*/
OutputStream os = clientSocket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
PrintWriter pw = new PrintWriter(osw, true);
while (true) {
pw.println(scanner.nextLine());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (clientSocket != null) {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* 该线程体用来循环读取服务端发送过来的信息 并输出到客户端的控制台
*
* @param args
*/
class GetServerMsgHandler implements Runnable {
@Override
public void run() {
try {
InputStream is = clientSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String msgString = null;
while ((msgString = br.readLine()) != null) {
System.out.println("服务端提示:" + msgString);
}
} catch (Exception e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
ChatClient client = new ChatClient();
client.start();
}
}
package com.etc; 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.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 控制台聊天程序 服务端应用程序
*
*
*/
public class chatServer {
/**
* ServerSocket 是运行在服务端的Socket 用来监听端口,等待客户端的连接, 一旦连接成功就会返回与该客户端通信的Socket
*/
private ServerSocket serverSocket;
/**
* 创建线程池来管理客户端的连接线程 避免系统资源过度浪费
*/
private ExecutorService threadPool;
/**
* 该属性用来存放客户端之间私聊的信息
*/
private Map<String, PrintWriter> allOut; /**
* 构造方法,服务端初始化
*/
public chatServer() {
try {
/*
* 创建ServerSocket,并申请服务端口 将来客户端就是通过该端口连接服务端程序的
*/
serverSocket = new ServerSocket(12580);
/*
* 初始化Map集合,存放客户端信息
*/
allOut = new HashMap<String, PrintWriter>();
/*
* 初始化线程池,设置线程的数量
*/
threadPool = Executors.newFixedThreadPool(10);
/*
* 初始化用来存放客户端输出流的集合, 每当一个客户端连接,就会将该客户端的输出流存入该集合; 每当一个客户端断开连接,就会将集合中该客户端的输出流删除;
* 每当转发一条信息,就要遍历集合中的所有输出流(元素) 因此转发的频率高于客户端登入登出的频率,
* 还是应该使用ArrayList来存储元素,仅限群聊,私聊不行 allOut = new ArrayList<PrintWriter>();
*/
} catch (Exception e) {
e.printStackTrace();
}
} /*
* 将客户端的信息以Map形式存入集合中
*/
private void addOut(String key, PrintWriter value) {
synchronized (this) {
allOut.put(key, value);
}
} /*
* 将给定的输出流从共享集合中删除 参数为客户端nickName,作为Map的key键
*/
private synchronized void removeOut(String key) {
allOut.remove(key);
System.out.println("当前在线人数为:" + allOut.size());
} /*
* 将给定的消息转发给所有客户端
*/
private synchronized void sendMsgToAll(String message) {
for (PrintWriter out : allOut.values()) {
out.println(message);
System.out.println("当前在线人数为:" + allOut.size());
}
} /*
* 将给定的消息转发给私聊的客户端
*/
private synchronized void sendMsgToPrivate(String nickname, String message) {
PrintWriter pw = allOut.get(nickname); // 将对应客户端的聊天信息取出作为私聊内容发送出去
if (pw != null) {
pw.println(message);
System.out.println("当前在线私聊人数为:" + allOut.size());
}
} /**
* 服务端启动的方法
*/
public void start() {
try {
while (true) {
/*
* 监听10086端口
*/
System.out.println("等待客户端连接... ... ");
/*
* Socket accept() 这是一个阻塞方法,会一直在10086端口进行监听
* 直到一个客户端连接上,此时该方法会将与这个客户端进行通信的Socket返回
*/
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功! ");
/*
* 启动一个线程,由线程来处理客户端的请求,这样可以再次监听 下一个客户端的连接了
*/
Runnable run = new GetClientMsgHandler(socket);
threadPool.execute(run); // 通过线程池来分配线程
}
} catch (Exception e) {
e.printStackTrace();
}
} /**
* 该线程体用来处理给定的某一个客户端的消息,循环接收客户端发送 的每一个字符串,并输出到控制台
*
* @author Jacob
*
*/
class GetClientMsgHandler implements Runnable {
/*
* 该属性是当前线程处理的具体的客户端的Socket
*
* @see java.lang.Runnable#run()
*/
private Socket socket;
/*
* 获取客户端的地址信息 private String hostIP;
*/
/*
* 获取客户端的昵称
*/
private String nickName; /*
* 创建构造方法
*/
public GetClientMsgHandler(Socket socket) {
this.socket = socket;
/*
* 获取远端客户的Ip地址信息 保存客户端的IP地址字符串 InetAddress address = socket.getInetAddress();
* hostIP = address.getHostAddress();
*/
} /*
* 创建内部类来获取昵称
*/
private String getNickName() throws Exception {
try {
// 服务端的输入流读取客户端发送来的昵称输出流
InputStream iin = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(iin, "UTF-8");
BufferedReader bReader = new BufferedReader(isr);
// 服务端将昵称验证结果通过自身的输出流发送给客户端
OutputStream out = socket.getOutputStream();
OutputStreamWriter iosw = new OutputStreamWriter(out, "UTF-8");
PrintWriter ipw = new PrintWriter(iosw, true);
// 读取客户端发来的昵称
String nameString = bReader.readLine();
while (true) {
if (nameString.trim().length() == 0) {
ipw.println("FAIL");
}
if (allOut.containsKey(nameString)) {
ipw.println("FAIL");
} else {
ipw.println("OK");
return nameString;
}
nameString = bReader.readLine();
}
} catch (Exception e) {
throw e;
}
} @Override
public void run() {
PrintWriter pw = null;
try {
/*
* 通过客户端的Socket获取客户端的输出流 用来将消息发送给客户端
*/
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
pw = new PrintWriter(osw, true);
/*
* 将客户昵称和其所说的话作为元素存入共享集合HashMap中
*/
nickName = getNickName();
addOut(nickName, pw);
Thread.sleep(100);
/*
* 服务端通知所有客户端,某用户登录
*/
sendMsgToAll("[系统通知]:欢迎**" + nickName + "**登陆聊天室!");
/*
* 通过客户端的Socket获取输入流 读取客户端发送来的信息
*/
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String msgString = null;
while ((msgString = br.readLine()) != null) {
// 验证是否是私聊
if (msgString.startsWith("@")) {
/*
* 私聊格式:@昵称:内容
*/
int index = msgString.indexOf(":");
if (index >= 0) {
// 获取昵称
String name = msgString.substring(1, index);
String info = msgString.substring(index + 1, msgString.length());
info = nickName + "对你说:" + info;
// 将私聊信息发送出去
sendMsgToPrivate(name, info);
// 服务端不在广播私聊的信息
continue;
}
}
/*
* 遍历所有输出流,将该客户端发送的信息转发给所有客户端
*/
System.out.println(nickName + "说:" + msgString);
sendMsgToAll(nickName + "说:" + msgString);
}
} catch (Exception e) {
/*
* 因为Win系统用户的客户端断开连接后,br.readLine()方法读取 不到信息就会抛出异常,而Linux系统会持续发送null;
* 因此这里就不在将捕获的异常抛出了。
*/
} finally {
/*
* 当执行到此处时,说明客户端已经与服务端断开连接 则将该客户端存在共享集合中的输出流删除
*/
removeOut(nickName);
/*
* 通知所有客户端,某某客户已经下线
*/
sendMsgToAll("[系统通知]:" + nickName + "已经下线了。");
/*
* 关闭socket,则通过Socket获取的输入输出流也一同关闭了
*/
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
} public static void main(String[] args) {
chatServer server = new chatServer();
server.start();
}
}
java 网络编程Socket的更多相关文章
- java网络编程socket\server\TCP笔记(转)
java网络编程socket\server\TCP笔记(转) 2012-12-14 08:30:04| 分类: Socket | 标签:java |举报|字号 订阅 1 TCP的开销 a ...
- java网络编程socket解析
转载:http://www.blogjava.net/landon/archive/2013/07/02/401137.html Java网络编程精解笔记2:Socket详解 Socket用法详解 在 ...
- Java网络编程——Socket
网络是连接不同计算机的媒介,不同的计算机依靠网络来互相通信,即传递数据. Java中与网络编程相关的部分主要是Socket(套接字),它作为一种抽象的结构,实现了与通信相关的各类方法,构成一套完整的通 ...
- day05 Java网络编程socket 与多线程
java网络编程 java.net.Socket Socket(套接字)封装了TCP协议的通讯细节,是的我们使用它可以与服务端建立网络链接,并通过 它获取两个流(一个输入一个输出),然后使用这两个流的 ...
- java网络编程Socket通信详解
Java最初是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中,使用最多的就是Socket.像大家熟悉的QQ.MSN都使用了Socket相关的技术. ...
- java网络编程——socket实现简单的CS会话
还记得当年学计网课时用python实现一个简单的CS会话功能,这也是学习socket网络编程的第一步,现改用java重新实现,以此记录. 客户端 import java.io.*; import ja ...
- JAVA网络编程Socket常见问题 【长连接专题】
一. 网络程序运行过程中的常见异常及处理 第1个异常是 java.net.BindException:Address already in use: JVM_Bind. 该异常发生在服务器端进行new ...
- java 网络编程Socket编程
Server.java import java.io.*; import java.net.*; public class Server { public static void main(Strin ...
- Java网络编程Socket通信
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的.可靠的.基于字节流的传输层通信协议 UDP (User Datagram Proto ...
随机推荐
- GDSOI2017第三轮模拟4.21 总结
1 第一题看着就觉得猎奇,于是就想着打暴力就跑. 但是很严重的问题就是... \(D\)和\(B\)打反了,都不知道当时在干什么??? 原本可以拿35. 2 第二题看着就觉得套路,于是想着今天就攻这题 ...
- POJ 2078
16ms 解法: #include <cstdio> //using namespace std; ][]; ][]; ]; ]; int n,min,max; void solve(in ...
- MySQL用户管理+MySQL权限管理
我们现在默认使用的都是root用户,超级管理员,拥有全部的权限! 但是,一个公司里面的数据库服务器上面可能同时运行着很多个项目的数据库! 所以,我们应该可以根据不同的项目建立不同的用户,分配不同的权限 ...
- Permutations 全排列 回溯
Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...
- Directx11教程(53) D3D11管线(8) GS的调度执行
原文:Directx11教程(53) D3D11管线(8) GS的调度执行 在前面的教程中,我们分析了VS-PS的shader管线组合执行过程,本章我们分析一下VS-GS-PS的管线执行 ...
- Linux进程管理(三、 线程)
// ---- refer glibc, pthread_create.c ----// int __pthread_create_2_0 (newthread, attr, start_routin ...
- bzoj2424 订货
Description 某公司估计市场在第i个月对某产品的需求量为Ui,已知在第i月该产品的订货单价为di,上个月月底未销完的单位产品要付存贮费用m,假定第一月月初的库存量为零,第n月月底的库存量也为 ...
- 装了ubuntu之后,只能进入ubuntu系统,不能进入windows系统
电脑之前安装的是Windows 7系统, 后来在安装Linux系统中(快要完成)出现了故障, 没办法只能关机,之后重启,重启后只能进入Linux系统了 解决方案: 使用sudo update-grub ...
- shell脚本练习题(更新中...)
练习题(这里贴的是自己写的代码, 网上给的题目代码我会附加在最下面) 1. 编写shell脚本,计算1-100的和: #!/bin/bash #caculate the to `; do sum=$[ ...
- Java练习 SDUT-4303_简单的复数运算(类和对象)
简单的复数运算(类和对象) Time Limit: 2000 ms Memory Limit: 65536 KiB Problem Description 设计一个类Complex,用于封装对复数的下 ...