Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
1.Socket是一个网络通信的套接字(接口)
2.socket实现过程:服务器端先初始化Socket,然后与端口绑定(bind), 对端口进行监听(listen),调用accept阻塞,等待客户端连接。
在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),
如果连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,
最后关闭连接,一次交互结束。
1)配置:
服务器:
a.socket()创建socket对象
b.bind()为socket对象绑定协议,赋予名字
c.listen()监听此socket,将socket从默认主动类型改为被动类型
客户端:
a.socket()创建一个socket对象
b.connect()连接服务器的socket对象
2)服务器,客户端都可使用read(),write()等函数进行通信
采用Java语言编写的socket服务端
采用Android编写的socket客户端
演示图片如下:
目标:实现上图演示的功能:
1.创建主函数,功能:首先创建服务并监听请求,在根据输入的内容执行不同的功能.
以下只提供了部分代码如下:
public static void main(String[]args) throws IOException { //主入口函数.
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line; boolean isExit = false;//变量初始化
ClientManager.startServer();//开启服务器并监听请求 服务器发送数据(线程
while (!isExit){ line = br.readLine();
if (line.startsWith("exit")){ break; }//退出程序
if (line.startsWith("send")){ sendMessage(line);}//发送数据至客户端
else if (line.startsWith("save")) { SaveToFile(line);}//保存客户端数据到某一个文件
else if (line.startsWith("list")) { printTotal();}//获取连接列表
else if (line.startsWith("all")) { allSendMsg(line);}//发送消息到所有的客户端连接
else { System.out.println("输入错误 请重新输入");} }
ClientManager.shutDown(); // 关闭 清空
}
2.实现socket服务端的构造方法及监听事件的线程操作.
public class ClientManager {
public static String text_what=" "; //客户端向服务端发送的消息..
private static ServerThread serverThread = null;
private static int sum = 0; //连接数量
private static Map<String, Socket> clientMap = new HashMap<String, Socket>();
private static List<String> clientList = new ArrayList<String>();
private static class ServerThread implements Runnable {
private ServerSocket server;
private int port = 9896;//54321;
private boolean isExit = false;// 一个boolean类型的判断 默认是退出状态false
// 构造方法初始化
public ServerThread() {//启动server
try {
server = new ServerSocket(port); //启动server及端口号 //ServerSocket的构造方法.
System.out.println("启动server,端口号:" +server.getLocalPort()); }
catch (IOException e) { e.printStackTrace();}
}
public void run() {
try {
while (!isExit) {
// 等待连接
System.out.println("等待手机的连接中... ...");
final Socket socket = server.accept(); //等待客户端的连接,当客户端请求连接时,返回一个Socket.
System.out.println("获取的手机IP地址及端口号:" + socket.getRemoteSocketAddress().toString());
//因为考虑到多手机连接的情况 所以加入线程锁 只允许单线程工作
new Thread(new Runnable() {
private String text;
public void run() {
try {
synchronized (this) {++sum; // 在这里考虑到线程总数的计算 也代表着连接手机的数量
// 存入到集合和Map中为群发和单独发送做准备
String string = socket.getRemoteSocketAddress().toString();
clientList.add(string); clientMap.put(string, socket); }
// 定义输入输出流
InputStream is = socket.getInputStream();
// 接下来考虑输入流的读取显示到PC端和返回是否收到
byte[] buffer = new byte[102400]; int len;//(1kb数据以内有效.)
while ((len = is.read(buffer)) > -1) { //不断读取来自客户端的消息
text = new String(buffer, 0, len);
text=new String(text.getBytes("GBK"));
new net_untils().saveTofileMode("socket_list",text,true,0);
int net_mode=-1;
if ((new net_untils().isInteger(text))&&(!text.equalsIgnoreCase("0")))
{net_mode=Integer.parseInt(text); }//若收到的数据含有数字则不再显示直接根据固定对应方式回应客户端.
else System.out.println("收到的数据为:" + text); text_what=text;//(客户端之间交流时得到的数据)
//收到的特殊字符串后若为保存文件的格式则自动保存文件.
if (net_mode==1) sendMsgAll("(自动回复:获取该服务器系统信息)\n本服务器链接数量为"+sumTotal()+
"\n本服务器的IP地址和端口为:"+socket.getLocalSocketAddress().toString()+
"\n连接的客户端IP地址和端口为"+socket.getRemoteSocketAddress().toString()
);
if (net_mode==2) sendMsgAll( new net_untils().Read_form_file("工作任务.txt")); //自动读取固定文件数据
if (net_mode==9) sendMsgAll( MU_img() ); //向客户端发送某路径下的文件夹
if (text.substring(0,1).equalsIgnoreCase("0")) {
new net_untils().saveTofile("工作任务.txt","系统消息(已保存至重要文件)");}
if (text.substring(0, 1).equalsIgnoreCase("8")) {
String wht=new net_untils().Str_lastOne(text);
switch (wht) {
case "txt": //向客户端发送文件内的文本..
wht=new net_untils().Read_form_path( text.substring(1, text.length()) );
sendMsgAll("已获取文件:\n"+wht); break;
case "mp3": break;
case "wav": //在本设备内播放.wav的文件..
try { new untils().music(text.substring(1, text.length()) );}
catch (Exception e) { e.printStackTrace();}
break;
default:
wht=MU_path( text.substring(1, text.length()) );
sendMsgAll(wht ); break;
}
}
} }
catch (IOException e) { e.printStackTrace();}
finally {
System.out.println("关闭连接:" + socket.getRemoteSocketAddress().toString());
synchronized (this) { --sum;
String string = socket.getRemoteSocketAddress().toString();
clientMap.remove(string); clientList.remove(string); } }
} }).start(); } }
catch (IOException e) { e.printStackTrace();}
}
// 关闭server
public void stop() {
isExit = true; if (server != null) {
try { server.close(); //关闭服务器socket
System.out.println("已关闭server"); }
catch (IOException e) { e.printStackTrace();} }
}
}
public static ServerThread startServer() { // 功能:启动server
System.out.println("开启server");
if (serverThread != null) { //变量不为空时
System.out.println("server不为null正在重启server");
shutDown(); } // 以下为关闭server和socket
// 初始化
serverThread = new ServerThread();//启动Ip及端口,不断监听请求并作出响应.
new Thread(serverThread).start();//开启线程
System.out.println("开启server成功");
return serverThread;
}
}
3.可以通过这个类读取服务端的文件,我是在电脑E:/盘中创建的文件夹,这个文件夹专门作为服务器与客户端通信内容的文件,大家可以随意创建,
public class net_untils {
public String Str_lastOne(String wht) { //获取字符串的后缀名,即'.'字符之后的内容
StringBuffer strbuf=new StringBuffer(wht);
String strnf="";
for (int i=0;i<strbuf.length();i++) {
char whr=strbuf.charAt(i);
if (whr=='.') { strnf=strbuf.substring(i+1, strbuf.length()).toString();
break;} }
return strnf;
}
public static boolean isInteger(String str) {
Pattern pattern=Pattern.compile("[0-9]");//^[-\\+]?[\\d]*$
return pattern.matcher(str).matches();
}
public void saveTofile(String filename,String tip) throws IOException {//保存文件并给出提示消息
OutputStream ous = null;
try {
ous = new FileOutputStream(new File("E://personal//save_data/",filename));//同名会覆盖
ous.write(ClientManager.text_what.getBytes());
System.out.println(tip);}
catch (FileNotFoundException e) { e.printStackTrace();ous.close();}
}
public void saveTofileMode(String filename,String filecontent,boolean append,int listname) {
FileWriter fw=null;
try {String tip=null;
fw=new FileWriter("E://personal//save_data/"+filename,append);
if (listname==-1) tip="Server:"; else if (listname==0) tip="Client:"; else tip=String.valueOf(listname);
fw.write(tip+filecontent+"\n"); }
catch (Exception e) { System.out.println("书写日志发生错误:"+e.toString());}
finally { try {fw.close();} catch (IOException r) {r.printStackTrace();} }
}
public String Read_form_file(String filename) {//文件打开方式需要GBK模式
java.io.File file = new java.io.File("E:/personal/save_data/"+filename);
FileInputStream in = null;
if (!file.exists()){ return "file is not found";}
//如果文件不存在,我们就抛出异常或者不在继续执行
//在实际应用中,尽量少用异常,会增加系统的负担
try {
in = new FileInputStream(file);
byte bytArr[] = new byte[1024];
int len = in.read(bytArr);
String fstr=new String(bytArr, 0, len);
in.close(); fstr=new String (fstr.getBytes("GB2312"));
return fstr;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public String Read_form_path(String filename) {//文件打开方式需要GBK模式
java.io.File file = new java.io.File(filename);
FileInputStream in = null;
if (!file.exists()){ return "file is not found";}
//如果文件不存在,我们就抛出异常或者不在继续执行
//在实际应用中,尽量少用异常,会增加系统的负担
try {
in = new FileInputStream(file);
byte bytArr[] = new byte[1024];
int len = in.read(bytArr);
String fstr=new String(bytArr, 0, len);
in.close(); fstr=new String (fstr.getBytes("GB2312"));
return fstr;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
可以在其中添加很多有趣的功能,如通过客户端播放服务器文件下的音视频文件等等…
socket通信的服务端到此就介绍完了,这里只贴上了部分代码,感兴趣的话下方有下载链接
首先就是新建Android应用,注意要在Manifest.xml文件中添加访问internet的权限,在res中创建layout和添加图片
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="600px"
android:orientation="horizontal">
<TextView
android:id="@+id/texr"
android:layout_width="300px"
android:layout_height="600px"
android:scrollbars="vertical"
android:hint="请输入指令:"/>
<TextView android:id="@+id/texd"
android:layout_width="fill_parent"
android:layout_height="600px"
android:hint="设备文本"/>
</LinearLayout>
<!--android:ellipsize="marquee"
android:singleLine="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:inputType="textMultiLine" -->
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/eidt"
android:layout_width="500px"
android:layout_height="wrap_content"
android:hint="(数字开头代表某功能)"/>
<Button
android:id="@+id/shujushangchuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="发送消息"/>
<Button android:id="@+id/listen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"
android:onClick="clik"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:gravity="right"
android:layout_height="wrap_content"
android:background="@drawable/footer_bar">
<Button android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="关闭连接"
android:onClick="coo"/>
<Button android:id="@+id/connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="重新连接"
android:onClick="coo"/>
<TextView android:id="@+id/te_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="初始化.."
android:background="#FF000000"
android:textColor="#FFFFFFFF"/>
<ImageButton
android:id="@+id/re"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/back"
android:layout_gravity="right"/>
</LinearLayout>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<LinearLayout android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button android:id="@+id/co_pa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电脑文件路径"/>
</LinearLayout>
<LinearLayout
android:layout_width="700px"
android:layout_height="fill_parent"
android:orientation="vertical">
<TextView android:id="@+id/sh_path"
android:layout_width="700px"
android:layout_height="200px" />
<ListView android:id="@+id/sh_ew"
android:layout_width="700px"
android:visibility="gone"
android:layout_height="fill_parent"></ListView>
<Spinner
android:id="@+id/spinner2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
2.socket客户端:
…注释已经很完整了
public class And_client extends Activity {
private TextView chatmessage=null; //文本视图显示聊天记录.(显示数据交流过程)
private EditText sendmessage=null; //用户输入编辑框.(数据源可以是编辑框或者是文件)
private Button send_button=null,lis_bu; //发送按钮.(发送数据的行为)
private static String HOST="192.168.0.108"; //服务器的IP地址.
private static int PORT=9896; //服务器的端口号.
private Socket socket=null; //声明套接字类,传输数据.
private String mess_s=""; //待保存的数据.
private Spinner spinner;
private ArrayAdapter<String> aspnStars;
private String Rpath="";
private List<String> paths=null; //存放路径
private String rootPath="E:/personal/save_data/name/";//服务端设备的起始目录
private TextView mPath; //显示正在访问的路径..
private TextView te_show; //显示相关状态消息..
private TextView te_text; //显示设备文件文本..
TextToSpeech mtts;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
chatmessage=(TextView)findViewById(R.id.texr);
chatmessage.setMovementMethod(ScrollingMovementMethod.getInstance());
sendmessage=(EditText)findViewById(R.id.eidt); //将服务器作为手机访问的一部分.
send_button=(Button)findViewById(R.id.shujushangchuan);
spinner=(Spinner)findViewById(R.id.spinner2); spinner.setVisibility(View.INVISIBLE);
mPath=(TextView)findViewById(R.id.sh_path); mPath.setText("服务器文件路径为:");
te_show=(TextView)findViewById(R.id.te_show);
te_text=(TextView)findViewById(R.id.texd);
lis_bu=(Button)findViewById(R.id.listen); lis_bu.setVisibility(View.GONE);
mtts=new TextToSpeech(And_client.this,new TTSListener());
initSocket();
send_button.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String message=sendmessage.getText().toString(); sendmessage.setText("");
new ClientWrite(socket, message).start();//作用类似于sendMsg(message);//发送消息
new Tool_2().txtviw_ad(chatmessage, "Client:"+message);
new Tool_2().reshtew(chatmessage, chatmessage.getText().toString());
} });
ImageButton re=(ImageButton)findViewById(R.id.re);
re.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
});
final Button sedf=(Button)findViewById(R.id.co_pa);
sedf.setOnClickListener(new OnClickListener() { //获取电脑某路径下的文件列表
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
String message="9";//+"获取服务器文件";
sedf.setEnabled(false);
new ClientWrite(socket, message).start();
new Tool_2().txtviw_ad(chatmessage, "Client(系统消息):已发送重要消息");
new Tool_2().reshtew(chatmessage, chatmessage.getText().toString());
spinner.setVisibility(View.VISIBLE);
//ReNoList(1,mess_s);
ReSpinner(mess_s);
}
});
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
//String[] res=getResources().getStringArray(R.array.Stars);
String str=(String)arg0.getItemAtPosition(arg2);
if (str.equalsIgnoreCase("请选择")) { ;}
else {
String str_la=new Tool_Str().Str_lastOne(str);
switch (str_la) {
case "txt":
case "mp3":
case "wav":
rootPath+=str; te_show.setText("正在执行文件");
break;//若不是文件夹..
default: str_la="不是";
te_show.setText("正在访问文件夹"); break;
}
//路径末尾带有'/'的处理..
if (str_la.equalsIgnoreCase("不是")) {
if (arg2==1) { rootPath="E:/personal/save_data/name/";} //回到根目录
else if (arg2==2) { rootPath=new Tool_Str().last_path(rootPath);} //回到上一目录
else rootPath+=str+"/"; } //若是文件夹
//发送带有加前头为8的路径,向服务器说明是访问服务器设备文件..
String message="8"+rootPath; mPath.setText(message);
new ClientWrite(socket, message).start();
new Tool_2().txtviw_ad(chatmessage, "Client(系统消息):已发送重要消息");
new Tool_2().reshtew(chatmessage, chatmessage.getText().toString());
new tool_utils.thread_utils().sleepForInSecs(1); //有等待时间的??????防止socket错乱????????????????
//服务器向客户端发送的消息:..
switch (str_la) {
case "txt": showDialog(1);
//rootPath=new Tool_Str().last_path(rootPath);
break; //显示设备文件文本内容..
case "mp3":
case "wav":
//rootPath=new Tool_Str().last_path(rootPath);
break;
default: te_text.setText(""); lis_bu.setVisibility(View.GONE);
spinner.setVisibility(View.VISIBLE);
ReSpinner(mess_s); break;
}
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
}); //注:使用列表读取数据存在延迟..可能绘制列表的时间要多
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
try { socket.close();
new Tool_2().showClickMessage(And_client.this,"已关闭连接",1); } //退出时自动关闭连接
catch (IOException e) { e.printStackTrace();}
}
public void coo(View v) {
switch (v.getId()) {
case R.id.close:
try { socket.close();
new Tool_2().showClickMessage(And_client.this,"已关闭连接",1); } //手动关闭连接.
catch (IOException e) { e.printStackTrace();}
break;
case R.id.connect:
initSocket(); new Tool_2().showClickMessage(And_client.this, "已重新连接", 1); //重新连接.
break;
}
}
public void initSocket() { //初始化socket
final Handler handler=new MyHandler();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
socket=new Socket(HOST, PORT); //通过指定IP和端口号创建套接字.
InputStream inputStream = socket.getInputStream();// 接收来自服务器的消息
byte[] buffer = new byte[102400]; int len;
while ((len = inputStream.read(buffer)) != -1) {
String s = new String(buffer, 0, len);//开启的线程更新主UI线程
Message message = Message.obtain();
message.what = 1; message.obj = s;//线程IDwhat,线程数据obj
mess_s=s; handler.sendMessage(message); }
}
catch (Exception e) { e.printStackTrace(); } //打印异常并生成对话框.
} }).start();
}
public class MyHandler extends Handler { //=new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==1) {
final String s=(String)msg.obj;
runOnUiThread(new Runnable() {
public void run() {
new Tool_2().txtviw_ad(chatmessage, "Server:"+s);
new Tool_2().reshtew(chatmessage, chatmessage.getText().toString());
}
//string(读取数据后,若为保存文件的格式则自动保存文件)????????
//若读取数据为exit则退出该socket
}); }
}
};
private void actCMenu1(final String str) {
//增加文件方法
LayoutInflater factory=LayoutInflater.from(And_client.this);
View myView=factory.inflate(R.layout.rename_alert_dialog,null);
final EditText myEditText=(EditText)myView.findViewById(R.id.mEdit);
myEditText.setHint("文件名"); //对文件名进行检查防止文件重名
android.content.DialogInterface.OnClickListener listener2=new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { /* 取得修改后的文件路径 */
final String filenae=myEditText.getText().toString();
if (mess_s.equalsIgnoreCase("")) {
new Tool_2().showClickMessage(And_client.this, "保存失败",1);
}
else new Tool_1().writeFileToPa(str, filenae+".txt", mess_s, 1);
} }; /* 重新产生文件列表的 ListView */
AlertDialog newFileDialog=new AlertDialog.Builder(And_client.this).create(); /* create 更改档名时跳出的 Dialog */
newFileDialog.setView(myView); newFileDialog.setButton("确定",listener2); /* 设置更改档名点击确认后的 Listener */
newFileDialog.setButton2("取消",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) { ;} }); newFileDialog.show();
}
//将定义的mStars数据载入到Spinner1组件
private void ReSpinner(String mbpath) { //加载传回的内容..
spinner.setVisibility(View.VISIBLE); spinner.setSelection(0);
paths=new ArrayList<String>();
paths.add("请选择"); paths.add("根目录");
if (new Tool_Str().cha_num(mPath.getText().toString(), '/')>1) {
paths.add("上一层"); }
StringBuffer R_path=new StringBuffer(mbpath);
StringBuffer S_path=new StringBuffer(10000);
for (int i=0,j=1;i<R_path.length();i++) {
char pa=R_path.charAt(i);
if (pa=='\n') {
j+=1; paths.add(S_path.toString());
S_path=new StringBuffer(10000); }
else S_path.append(pa);
}
aspnStars=new ArrayAdapter<String>(And_client.this,android.R.layout.simple_spinner_item,paths);
aspnStars.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(aspnStars);
//txte.setText( String.valueOf( spinner.getAdapter().getCount()) );
}
private class TTSListener implements OnInitListener{
public void onInit(int status){
if (status==TextToSpeech.SUCCESS){//设置语言
int result=mtts.setLanguage(Locale.CHINA);//若返回-2即不支持该语言
if (result==TextToSpeech.LANG_MISSING_DATA||result==TextToSpeech.LANG_NOT_SUPPORTED){ ;} //语言不可用,TTS使用失败
else { mtts.speak("", TextToSpeech.QUEUE_FLUSH,null);} } }
}
public void clik(View view) {
switch (view.getId()) {
case R.id.listen:
mtts.speak(te_text.getText().toString(),TextToSpeech.QUEUE_FLUSH, null);
}
}
public Dialog onCreateDialog(int id) {//对话框
switch (id) {
case 1:return buildDialog1(And_client.this); //规则展示项目.
}
return null;
}
private Dialog buildDialog1(final Context context) { //各个部门功能追求的理念信念 设定..
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("正在打开文件..");builder.setIcon(R.drawable.clock);
builder.setMessage("打开");
builder.setPositiveButton("打开", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {
// TODO Auto-generated method stub
lis_bu.setVisibility(View.VISIBLE); te_text.setText(mess_s);
}
})
.setNegativeButton("返回",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int whichButton) { ; } });
return builder.create();
}
}
3.我把写入socket的代码单独写入一个类中:
public class ClientWrite extends Thread{
private Socket client;
private String mess;
public ClientWrite(Socket client,String mess) {
this.client=client;
this.mess=mess; }
public void run() {
try {
OutputStream outputStream = null;
outputStream = client.getOutputStream();
outputStream.write((mess).getBytes("GBK"));//("utf-8"));
outputStream.flush();// 清空缓存
}
catch (Exception e) { e.printStackTrace();}
}
}
socket通信的客户端到此就介绍完了,这里只贴上了部分代码,感兴趣的话下方有下载链接
能够成功运行的条件:
1.客户端服务端都是在同一网络环境下,涉及在不同网络下通信还需要什么ip映射之类的知识,下次我在详细说明.
2.客户端涉及的ip地址是在命令提示符中获取的.
遇到的问题:
socket通信会涉及到编码问题,关于编码问题请在下载链接中获取
服务端采用线程和监听列表组成,因为可能会有多个客户端连接
改进功能:
1.通信内容会有数据大小的限制,可以将数据分割成几个包再进行发送
2.丰富服务端的功能,如自动浏览文件夹下的图片音视频等等…
3.读取服务端内容时由于网络原因有时会出现内容接收不到或者延迟接收等等情况
4.可以在客户端提供下载服务端文件夹下的图片等等…
链接: socket服务端
链接: socket客户端
我也仅仅是实现了socket通信,也不是理解的特别明白,达不到那种
"知其然在知其所以然"的境界…