欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用socket写一个简单的聊天程序&碰到的问题

程序员文章站 2022-04-24 14:32:55
...

程序分成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