首先:accept()和Read()方法都有阻塞特性
结合多线程实现socket
在Socket技术中,常用的实践方式就是Socket结合Thread多线程技术,客户端每发起一次新的请求,就把这个请求交给新创建的线程来执行这次业务。当然,如果使用线程池技术,则会更加高效。本示例先使用原始的非线程池来进行演示。
//socket类提供了两个方法用于得到输入流和输出流,分别是getInputStream()和getOutputStream() 可以对其进行包装
//例如:Socket socket = new Socket(“localhost”,8189);
// PrintStream oStream = new PrintStream( new BufferedOutputStream(socket.getOutputStream()));
//DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
//BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
一个使用DataInputStream的简单例子:
public static void main(String [] args)
{
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try
{
System.out.println("连接到主机:" + serverName + " ,端口号:" + port);
Socket client = new Socket(serverName, port);
System.out.println("远程主机地址:" + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("服务器响应: " + in.readUTF());
client.close();
}catch(IOException e)
{
e.printStackTrace();
}
}
使用非线程池(拓展Thread)
beginthread:
//多线程实现socket
public class BeginThread extends Thread{
private Socket socket;
public BeginThread(Socket socket)
{
super();
this.socket = socket;
}
@Override
public void run(){
try{
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
char[] charArr = new char[1000];
int readLen = -1;
while((readLen = (inputStreamReader.read(charArr))) != -1)
{
String newString = new String(charArr,0,readLen);
System.out.println(newString);
}
inputStreamReader.close();
inputStream.close();//关闭两个流
socket.close();//关闭socket
}catch (IOException e){
e.printStackTrace();
}
}
}
beginserver类:
public class BeginServer {
public static void main(String[]args) throws IOException {
ServerSocket socket = new ServerSocket(8888);
int runTag = 1;
while (runTag == 1)
{
Socket socket1 = socket.accept();
BeginThread beginThread = new BeginThread(socket1);
beginThread.start();
}
socket.close();
}
}
BeginClient :
public class BeginClient {
public static void main(String[]args) throws IOException {
Socket socket = new Socket("localhost",8888);
OutputStream outputStream = socket.getOutputStream();
// OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
// outputStreamWriter.write(String.valueOf("我是中国人".getBytes(StandardCharsets.UTF_8)));
outputStream.write("我是中国人yaoo".getBytes(StandardCharsets.UTF_8));
outputStream.close();
socket.close();
}
}
客户端每次发完一条数据就会关闭连接,而服务端一直会有线程接收请求。修改客户端的字符串,服务端结果:
由于 Socket socket1 = socket.accept();的阻塞特性,只有服务端受到客户端的信号之后才会开启一个新的线程。
使用线程池(Executor pool)
将以上代码修改为使用线程池:
beginThread只需要修改为
implements Runnable
而beginServer改为:
public class BeginServer {
private ServerSocket serverSocket;
private Executor pool;
public BeginServer(int port,int poolSize)
{
try{
serverSocket = new ServerSocket(port);
pool = Executors.newFixedThreadPool(poolSize);
}catch (IOException e)
{
e.printStackTrace();
}
}
public void startServer() {
try {
while (true) {
Socket socket = serverSocket.accept();
pool.execute(new BeginThread(socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[]args) throws IOException {
BeginServer beginServer = new BeginServer(8888,10000);
beginServer.startServer();
}
}
结果是一样的。
使用DatagramPacket DatagramSocket开发UDP/IP程序
DatagramPacket DatagramSocket是用来支持数据报通信的两个类,前者由于表示数据报,后者用于建立通信连接
写改程序首先需要建立一个DatagramSocket对象,用来接收或者发送数据报,然后以DatagramPacket 作为数据传输的载体。
使用UDP获取服务端时间
client:
public class UdpClient {
public void go() throws IOException, UnknownHostException{
DatagramPacket ingramPacket;
DatagramPacket outgramPacket;
DatagramSocket datagramSocket;
InetAddress serverAdress;
byte[]msg = new byte[100];//缓冲区
String receivedmsg;
datagramSocket = new DatagramSocket();
System.out.println("an udpclient ,datagramsocket:"+datagramSocket.getPort()+"localport:"+datagramSocket.getLocalPort());
serverAdress = InetAddress.getLocalHost();
outgramPacket = new DatagramPacket(msg,1,serverAdress,8000);//send to port 8000
datagramSocket.send(outgramPacket);//make the request to the server
ingramPacket = new DatagramPacket(msg,msg.length);//set up a datagram packet to receive server's response
datagramSocket.receive(ingramPacket);
receivedmsg = new String(ingramPacket.getData(),0,ingramPacket.getLength());
System.out.println(receivedmsg);//print the data received from the server
datagramSocket.close();
}
public static void main(String[]args) throws IOException {
UdpClient udpClient = new UdpClient();
try {
udpClient.go();
}catch (Exception e){
System.out.println(e);
}
}
}
server:
public class UdpServer {
public byte[]getTime(){
Date d = new Date();
return d.toString().getBytes(StandardCharsets.UTF_8);
}
public void go() throws IOException{
DatagramPacket inDataPacket;
DatagramPacket outDataPacket;
DatagramSocket datagramSocket;
InetAddress clientAdress;
int clientPort;
byte[]msg = new byte[10];//Incoming data buffer.ignored
byte[]time;
datagramSocket = new DatagramSocket(8000);//allocate a socket to man port 8000 for requests
System.out.println("UDPserver :"+datagramSocket.getPort()+"local is:"+datagramSocket.getLocalPort());
System.out.println("udpserver active on port 8000");
while (true)
{
inDataPacket = new DatagramPacket(msg,msg.length);
datagramSocket.receive(inDataPacket);//get the message
clientAdress = inDataPacket.getAddress();
clientPort = inDataPacket.getPort();
time=getTime();
outDataPacket = new DatagramPacket(time,time.length,clientAdress,clientPort);
datagramSocket.send(outDataPacket);//send the packet
}
}
public static void main(String[]args){
UdpServer udpServer = new UdpServer();
try{
udpServer.go();
}catch (IOException e)
{
e.printStackTrace();
}
}
}
结果:
客户端接收一次数据之后即关闭连接,而服务端一直可以接收请求。
其他
使用socket类作为客户端链接网站:
public class Hzy {
public static void main(String args[]) throws IOException {
Socket socket = null;
try {
socket = new Socket("www.csdn.net", 80);
System.out.println("socket连接成功");
} catch (IOException e) {
System.out.println("socket连接失败");
e.printStackTrace();
} finally {
socket.close();
}
}
}
注意:如果host为不存在的域名,会发生报错
用ServerSocket创建一个web服务器:
public class Hzy {
public static void main(String args[]) throws IOException {
ServerSocket serverSocket = new ServerSocket(6666,1, InetAddress.getByName("127.0.0.1"));
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String getString = "";
while (!"".equals(getString = bufferedReader.readLine())) {
System.out.println(getString);
}
OutputStream outputStream = socket.getOutputStream();
outputStream.write("HTTP/1.1 200 OK\r\n\r\n".getBytes());
outputStream.write(
"<html><body><a href='http://www.baidu.com'>i am baidu.com welcome you!</a></body></html>".getBytes());
outputStream.flush();
inputStream.close();
outputStream.close();
socket.close();
serverSocket.close();
}
}
这个代码不知道为什么打开浏览器还是没法访问,原因还未找到。
文件目录:
chartserver:
public class ChatServer {
private static final int SOCKET_PORT = 52000;
public static ArrayList<SocketBean> mSocketList = new ArrayList<SocketBean>();
private void initServer() {
try {
// 创建一个ServerSocket,用于监听客户端Socket的连接请求
ServerSocket server = new ServerSocket(SOCKET_PORT);
while (true) {
// 每当接收到客户端的Socket请求,服务器端也相应的创建一个Socket
SocketBean socket = new SocketBean(DateUtil.getTimeId(), server.accept());
mSocketList.add(socket);
// 每连接一个客户端,启动一个ServerThread线程为该客户端服务
new Thread(new ServerThread(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ChatServer server = new ChatServer();
server.initServer();
}
}
ServerThread
public class ServerThread implements Runnable {
private SocketBean mSocket = null;
private BufferedReader mReader = null;
public ServerThread(SocketBean mSocket) throws IOException {
this.mSocket = mSocket;
mReader = new BufferedReader(new InputStreamReader(mSocket.socket.getInputStream()));
}
@Override
public void run() {
try {
String content = null;
// 循环不断地从Socket中读取客户端发送过来的数据
while ((content = mReader.readLine()) != null) {
System.out.println("content="+content);
int pos = content.indexOf("|");
// 包头格式为:动作名称|设备编号|昵称|时间|对方设备编号
String head = content.substring(0, pos);
String body = content.substring(pos+1);
String[] splitArray = head.split(",");
String action = splitArray[0];
System.out.println("action="+action);
if (action.equals("LOGIN")) {
login(splitArray[1], splitArray[2], splitArray[3]);
} else if (action.equals("LOGOUT")) {
logout(splitArray[1]);
break;
} else if (action.equals("SENDMSG")) {
sendmsg("RECVMSG", splitArray[2], splitArray[4], splitArray[1], body);
} else if (action.equals("GETLIST")) {
getlist(splitArray[1]);
} else if (action.equals("SENDPHOTO")) {
sendmsg("RECVPHOTO", splitArray[2], splitArray[4], splitArray[1], body);
} else if (action.equals("SENDSOUND")) {
sendmsg("RECVSOUND", splitArray[2], splitArray[4], splitArray[1], body);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void login(String deviceId, String nickName, String loginTime) throws IOException {
for (int i=0; i<ChatServer.mSocketList.size(); i++) {
SocketBean item = ChatServer.mSocketList.get(i);
if (item.id.equals(mSocket.id)) {
item.deviceId = deviceId;
item.nickName = nickName;
item.loginTime = loginTime;
ChatServer.mSocketList.set(i, item);
break;
}
}
}
private String getFriend() {
String friends = "GETLIST,";
for (SocketBean item : ChatServer.mSocketList) {
if (item.deviceId!=null && item.deviceId.length()>0) {
String friend = String.format("|%s,%s,%s", item.deviceId, item.nickName, item.loginTime);
friends += friend;
}
}
return friends;
}
private void getlist(String deviceId) throws IOException {
for (int i=0; i<ChatServer.mSocketList.size(); i++) {
SocketBean item = ChatServer.mSocketList.get(i);
if (item.id.equals(mSocket.id) && item.deviceId.equals(deviceId)) {
PrintStream ps = new PrintStream(item.socket.getOutputStream());
ps.println(getFriend());
break;
}
}
}
private void logout(String deviceId) throws IOException {
for (int i=0; i<ChatServer.mSocketList.size(); i++) {
SocketBean item = ChatServer.mSocketList.get(i);
if (item.id.equals(mSocket.id) && item.deviceId.equals(deviceId)) {
PrintStream ps = new PrintStream(item.socket.getOutputStream());
ps.println("LOGOUT,|");
item.socket.close();
ChatServer.mSocketList.remove(i);
break;
}
}
}
private void sendmsg(String respAction, String otherName, String otherId, String selfId, String message) throws IOException {
for (int i=0; i<ChatServer.mSocketList.size(); i++) {
SocketBean item = ChatServer.mSocketList.get(i);
if (item.deviceId.equals(otherId)) {
String content = String.format("%s,%s,%s,%s|%s",
respAction, selfId, otherName, DateUtil.getNowTime(), message);
System.out.println("resp="+content);
PrintStream ps = new PrintStream(item.socket.getOutputStream());
ps.println(content);
break;
}
}
}
}
socketbean:
public class SocketBean {
public String id;
public Socket socket;
public String deviceId;
public String nickName;
public String loginTime;
public SocketBean(String id, Socket socket) {
this.id = id;
this.socket = socket;
this.deviceId = "";
this.nickName = "";
this.loginTime = "";
}
}
TestServer:
public class TestServer {
private static final int SOCKET_PORT = 51000;
private void initServer() {
try {
// 创建一个ServerSocket,用于监听客户端Socket的连接请求
ServerSocket server = new ServerSocket(SOCKET_PORT);
while (true) {
Socket socket = server.accept();
new Thread(new ServerThread(socket)).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestServer server = new TestServer();
server.initServer();
}
private class ServerThread implements Runnable {
private Socket mSocket;
private BufferedReader mReader;
public ServerThread(Socket socket) throws IOException {
mSocket = socket;
mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
}
@Override
public void run() {
try {
String content = null;
// 循环不断地从Socket中读取客户端发送过来的数据
while ((content = mReader.readLine()) != null) {
System.out.println("收到客户端消息:"+content);
PrintStream ps = new PrintStream(mSocket.getOutputStream());
ps.println("hi,很高兴认识你");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
客户端:
public class MessageTransmit implements Runnable {
private static final String TAG = "MessageTransmit";
// 以下为Socket服务器的ip和端口,根据实际情况修改
private static final String SOCKET_IP = "192.168.0.212";
private static final int SOCKET_PORT = 51000;
private Socket mSocket;
private BufferedReader mReader = null;
private OutputStream mWriter = null;
@Override
public void run() {
mSocket = new Socket();
try {
mSocket.connect(new InetSocketAddress(SOCKET_IP, SOCKET_PORT), 3000);
mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
mWriter = mSocket.getOutputStream();
// 启动一条子线程来读取服务器的返回数据
new RecvThread().start();
Looper.prepare();
Looper.loop();
} catch (Exception e) {
e.printStackTrace();
}
}
// 定义接收UI线程的Handler对象,App向后台服务器发送消息
public Handler mRecvHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage: "+msg.obj);
// 换行符相当于回车键,表示我写好了发出去吧
String send_msg = msg.obj.toString()+"\n";
try {
mWriter.write(send_msg.getBytes("utf8"));
} catch (Exception e) {
e.printStackTrace();
}
}
};
// 定义消息接收子线程,App从后台服务器接收消息
private class RecvThread extends Thread {
@Override
public void run() {
try {
String content = null;
while ((content = mReader.readLine()) != null) {
// 读取到来自服务器的数据
Message msg = Message.obtain();
msg.obj = content;
SocketActivity.mHandler.sendMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class ClientThread implements Runnable {
private static final String TAG = "ClientThread";
private static final String SOCKET_IP = "192.168.253.1";
//private static final String SOCKET_IP = "192.168.0.212";
private static final int SOCKET_PORT = 52000;
public static final String REQUEST_URL = "http://192.168.253.1:8080/UploadTest";
private Context mContext;
private Socket mSocket;
public Handler mRecvHandler;
private BufferedReader mReader = null;
private OutputStream mWriter = null;
public static String ACTION_RECV_MSG = "com.example.network.RECV_MSG";
public static String ACTION_GET_LIST = "com.example.network.GET_LIST";
public static String CONTENT = "CONTENT";
public static String SPLIT_LINE = "|";
public static String SPLIT_ITEM = ",";
public static String LOGIN = "LOGIN";
public static String LOGOUT = "LOGOUT";
public static String SENDMSG = "SENDMSG";
public static String RECVMSG = "RECVMSG";
public static String GETLIST = "GETLIST";
public static String SENDPHOTO = "SENDPHOTO";
public static String RECVPHOTO = "RECVPHOTO";
public static String SENDSOUND = "SENDSOUND";
public static String RECVSOUND = "RECVSOUND";
public ClientThread(Context context) {
mContext = context;
}
@Override
public void run() {
Log.d(TAG, "run");
mSocket = new Socket();
try {
Log.d(TAG, "connect");
mSocket.connect(new InetSocketAddress(SOCKET_IP, SOCKET_PORT), 3000);
mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
Log.d(TAG, "getOutputStream");
mWriter = mSocket.getOutputStream();
Log.d(TAG, "RecvThread");
// 启动一条子线程来读取服务器相应的数据
new RecvThread().start();
Looper.prepare();
// 定义接收UI线程的Handler对象,App向后台服务器发送消息
// 如果是在Application中启动线程,则mRecvHandler要在线程启动后才能初始化
// 并且要在Looper.prepare之后执行初始化动作
mRecvHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 接收到UI线程的用户输入的数据
try {
mWriter.write(msg.obj.toString().getBytes("utf8"));
} catch (Exception e) {
e.printStackTrace();
}
}
};
Looper.loop();
} catch (Exception e) {
e.printStackTrace();
notify(99, e.getMessage());
}
}
// 定义消息接收子线程,App从后台服务器接收消息
private class RecvThread extends Thread {
@Override
public void run() {
String content = null;
try {
while ((content = mReader.readLine()) != null) {
// 读取到来自服务器的数据之后,发送消息通知
ClientThread.this.notify(0, content);
}
} catch (Exception e) {
e.printStackTrace();
ClientThread.this.notify(97, e.getMessage());
}
}
}
private void notify(int type, String message) {
if (type == 99) {
String content = String.format("%s%s%s%s", "ERROR", SPLIT_ITEM, SPLIT_LINE, message);
Intent intent1 = new Intent(ACTION_RECV_MSG);
intent1.putExtra(CONTENT, content);
mContext.sendBroadcast(intent1);
Intent intent2 = new Intent(ACTION_GET_LIST);
intent2.putExtra(CONTENT, content);
mContext.sendBroadcast(intent2);
} else {
int pos = message.indexOf(SPLIT_LINE);
String head = message.substring(0, pos - 1);
String[] splitArray = head.split(SPLIT_ITEM);
String action = "";
if (splitArray[0].equals(RECVMSG)
|| splitArray[0].equals(RECVPHOTO)
|| splitArray[0].equals(RECVSOUND)) {
action = ACTION_RECV_MSG;
} else if (splitArray[0].equals(GETLIST)) {
action = ACTION_GET_LIST;
}
Log.d(TAG, "action=" + action + ", message=" + message);
Intent intent = new Intent(action);
intent.putExtra(CONTENT, message);
mContext.sendBroadcast(intent);
}
}
}