【Java笔记】网络编程基础

ServerSocket和Socket类:


ServerSocket是服务器用的“网络插口”可以定义,端口号等;Socket是客户端用的"网络插口",用来连接制定IP、端口等;

下面直接用代码和其中的注释演示一个网络通信的例子:在线聊天室;

package Test01;

/**
 * 群聊:Server:接收某一位成员的消息 转发给其他成员(收到消息才发出去 收发两个动作是单线程执行) Client:发送自己的消息 接受别的消息
 * (收发互不干扰 收消息与发消息应该使用两个单独的线程)
 * 
 * 服务器端 ServerSocket 1. 指定服务器这个程序在本机上的端口号(0~65535 0~1023(系统预先装订的程序占用了)) 2.
 * 监听客户端的连接 3. 交互
 * 
 */
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class Server {
	private static final int PORT = 20001;
	// 定义一个集合来放我们的客户端的socket对象,当做用户列表
	private static List<Socket> onLineUsers = new ArrayList<>();

	public static void main(String[] args) {

		ServerSocket server = null;
		try {
			// 用能传入端口的构造器穿件ServerSocket对象
			server = new ServerSocket(PORT);
			System.out.println("服务器已经准备好了   客户端可以连接了");
		} catch (IOException e) {
			// 这个异常是因为如果我们定义的端口已经被其他软件占用了,那么我们就不能成功连接
			System.out.println("端口被占用了!");
		}
		while (true) {
			Socket client = null;
			try {
				/*
				 * 监听客户端的连接请求,accept这个方法监听时,处于阻塞状态, 也就是说程序会停在这先不往下执行,直到监听到了连接请求
				 */
				client = server.accept();
			} catch (IOException e) {
				System.out.println("监听客户端时出现异常");
			}
			synchronized (onLineUsers) {
				// 这部分代码需要同步,因为我们的表单这个集合是共享变量;
				onLineUsers.add(client);
			}
			ServerThread serverThread = new ServerThread(client, onLineUsers);
			Thread t = new Thread(serverThread);
			t.start();
		}

	}
}
//写
class ServerThread implements Runnable {
	private Socket client;
	private List<Socket> onLineUsers;
	private DataInputStream dis;
	private String clientIp;
//定义构造器,参数有Socket对象,和表单集合
	public ServerThread(Socket client, List<Socket> onLineUsers) {
		this.client = client;
		this.onLineUsers = onLineUsers;
		this.clientIp = client.getInetAddress().getHostAddress();
		try {
			this.dis = new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("服务器取得用户输入流时出现异常,用户已下线");
			// 用户下线,我们把它从表单中删除
			synchronized (onLineUsers) {
				onLineUsers.remove(client);
			}
		}
	}

	@Override
	public void run() {
		String msg = null;
		DataOutputStream dos;
		System.out.println("欢迎用户" + clientIp + "进入聊天室");
		// 不断地收消息
		while (true) {
			if (dis != null) {
				try {
					msg = dis.readUTF();
				} catch (IOException e) {
					System.out.println("服务器读取用户消息时出现异常,用户已下线");
					synchronized (onLineUsers) {
						onLineUsers.remove(client);
					}
					break;
				}
				if (msg == null || msg.equals("")) {
					// 不转发消息
				} else {
					// 转发消息,需要遍历表单中每一个用户
					for (Socket s : onLineUsers) {
						//因为这个时候表单里已近add了此用户,此客户端没必要对自己也发一次消息,所以我们进行一次判断;如果不是再进行下一步操作
						if (s != client) {
							try {
								//Socket有getOutputStream的方法,我们创建对象并调用此方法将msg输入进流
								dos = new DataOutputStream(s.getOutputStream());
								dos.writeUTF(clientIp + ":" + msg);
							} catch (IOException e) {
								synchronized (onLineUsers) {
									onLineUsers.remove(s);
								}
							}
						}

					}

				}
			}

		}

	}

}

客户端如下:
package Test01;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端:Socket
 * 发消息  收消息
 */
public class Client {
	private static DataInputStream dis ;
	private static DataOutputStream dos;
	
	public static void main(String[] args) {
		Socket client =null;
		try {
			//创建对象的时候发起连接请求,其中localhost指本机ip地址,20001指端口,在这里我客户端和服务器都在我的电脑上运行;
			client = new Socket("localhost",20001);
		} catch (IOException e) {
			System.out.println("连接服务器失败");
		}
		
		try {
			//创建数据流输入输出的两个对象
			dis  = new DataInputStream(client.getInputStream());
			dos = new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {
			System.out.println("服务器端异常  客户端无法获取输入输出流!");
		}
		//开启线程
		RecMsg rec = new RecMsg(dis);
		SendMsg send = new SendMsg(dos);
		
		Thread recMsg = new Thread(rec);
		Thread sendMsg = new Thread(send);
		recMsg.start();
		sendMsg.start();
		
		
		
	}
}

class RecMsg implements Runnable{
	private  DataInputStream dis ;
	public RecMsg(DataInputStream dis) {
		this.dis = dis;
	}

	@Override
	public void run() {
		//收消息----输入流
		while(true){
			String msg = null;
			try {
				//读入数据,读入在流中的数据,注意read的格式应与服务器呼应
				msg = dis.readUTF();
			} catch (IOException e) {
				System.out.println("服务器端出现异常 用户读取消息失败");
			}
			
			System.out.println(msg);
			
		}
	}
	
}
class SendMsg implements Runnable{
	private  DataOutputStream dos;
	private Scanner scanner  = new Scanner(System.in);
	public SendMsg(DataOutputStream dos) {
		this.dos = dos;
	}

	@Override
	public void run() {
		//发消息----输出流
		while(true){
			String msg2Send = scanner.next();
			if(msg2Send!=null&&(!msg2Send.equals(""))){
				try {
					dos.writeUTF(msg2Send);
				} catch (IOException e) {
					System.out.println("服务器端出现异常 用户发送消息失败");	
				}
			}
		}
	}
	
}

输出结果:(我们先运行服务器,)

服务器显示:

服务器已经准备好了   客户端可以连接了
欢迎用户127.0.0.1进入聊天室
欢迎用户127.0.0.1进入聊天室
client1:

你好(此为输入)
127.0.0.1:嗯嗯,你好呀

client2:

127.0.0.1:你好
嗯嗯,你好呀(此为输入)



自己和自己聊天(笑),下次用swing等工具加上图形界面就更好玩啦


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值