使用socket写一个简单的聊天程序&碰到的问题
程序分成2个部分:
1,服务端,用来接受客户端发来的信息
2,客户端,用来向服务端发信息。
一、服务端如下:
SocketServerThread.java用来处理客户端发送的信息
package com.thread.socket.service;
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 SocketServerThread extends Thread {
// 和本线程相关的Socket
Socket socket = null;
public SocketServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
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);
String info = null;
while((info=br.readLine()) != null) { //循环读取客户端的信息
System.out.println(info);
}
socket.shutdownInput(); //关闭输入流
// 获取输出流,响应客户端的请求
os = socket.getOutputStream();
pw = new PrintWriter(os);
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
try {
if(pw!=null) pw.close();
if(os!=null) os.close();
if(br!=null) br.close();
if(isr!=null) isr.close();
if(is!=null) is.close();
if(socket!=null) socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerThread.java用来监听客户端请求,接受请求后开启线程调度SocketServerThread.java
package com.thread.socket.service;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 用来接受客户端发送的信息
*
*/
public class ServerThread extends Thread {
@SuppressWarnings("resource")
@Override
public void run() {
// 1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = null;
System.out.println("***服务器即将启动,等待客户端的连接***");
// 循环监听等待客户端的连接
while(true) {
// 调用accept()方法开始监听,等待客户端的连接
socket = serverSocket.accept();
// 创建一个新的线程,用来处理请求
SocketServerThread serverThread = new SocketServerThread(socket);
// 启动线程
serverThread.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
二、客户端如下:
Client.java
package com.thread.socket.client;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
/**
* 向服务端发送信息
*
* @param str
*/
public static void output(String str) {
try {
// 1.创建客户端Socket,指定服务器地址和端口
Socket socket = new Socket("localhost", 8008); // TODO 这里用的是本地的IP
// 2.获取输出流,向服务器端发送信息
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write(str);
pw.flush();
socket.shutdownOutput();//关闭输出流
//4.关闭资源
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、启动服务端
package com.thread.socket.main;
import java.util.Scanner;
import com.thread.socket.client.Client;
import com.thread.socket.service.ServerThread;
public class Chat {
@SuppressWarnings("resource")
public static void main(String[] args) {
// 启动服务器
ServerThread s = new ServerThread();
s.start();
// 向服务器发送信息
while (true) {
Scanner sb = new Scanner(System.in);
System.out.print("输入语句:");
String str = sb.nextLine();
Client.output(str);
}
}
}
注意:上面几个文件放在一起算一个程序。
将上述文件复制一份,做成另一个程序,开启这两个程序,就可以相互聊天了。
如果想在本地让这两程序相互发送消息的话,其中的一个程序的服务端要需要修改监听端口号,
另外一个程序的客户端要更改发送的端口号,这样两个程序才能在本地相互发送消息;
否则启动好一个程序后,再启动另一个程序,就会报该端口号被占用的错误。
---------------------------下面是本人搞事时,发现的bug-----------------------------
另外本人尝试连续不断的向服务端发送10000的请求,发现报错了
注意:设置成10000时,有时候会报错,不一定是每次都报错,如果想每次报错把这个10000改成100w试试。
报错信息如下:java.net.BindException: Address already in use: connect
这是别人的解释:https://blog.csdn.net/ywb201314/article/details/51258777
大致原因是端口号来不及释放,导致错误。
------------------------------------------------------------------------------------------
我多次测试之后,感觉应该是服务端接受请求上限导致的。
测试结果:服务端同时能接受的请求的上限是50条,
ServerSocket类部分源码定义如下(大概在229行左右):
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
setImpl();
if (port < 0 || port > 0xFFFF)
throw new IllegalArgumentException(
"Port value out of range: " + port);
if (backlog < 1)
backlog = 50;
try {
bind(new InetSocketAddress(bindAddr, port), backlog);
} catch(SecurityException e) {
close();
throw e;
} catch(IOException e) {
close();
throw e;
}
}
backlog这个参数解释是:请求链接队列的最大长度。
当你直接 new ServerSocket(port)时,默认是50。
从上面构造可以看出请求链接最大不能超过0xFFFF=2^16-1=65535
上一篇: ARP数据包发送
下一篇: JS上传大文件显示进度条实现方法