Linux C利用Socket套接字进行服务器与多个客户端进行通讯
来源:互联网 发布:怎么让胃变小 知乎 编辑:程序博客网 时间:2024/06/06 00:14
服务器端
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <string.h>#include <signal.h>#include <arpa/inet.h>#include <unistd.h>#include <pthread.h>//服务器端void *fun_thrReceiveHandler(void *socketInfo);void *fun_thrAcceptHandler(void *socketListen);//1:是 0:否int checkThrIsKill(pthread_t thr);typedef struct MySocketInfo{ int socketCon; char *ipaddr; uint16_t port;}_MySocketInfo;// 客户端数组struct MySocketInfo arrConSocket[10];int conClientCount = 0;// 接受客户端线程列表pthread_t arrThrReceiveClient[10];int thrReceiveClientCount = 0;int main(){ //初始化全局变量 //memset(arrConSocket,0,sizeof(struct MySocketInfo)*10); printf("开始socket\n"); /* 创建TCP连接的Socket套接字 */ int socketListen = socket(AF_INET, SOCK_STREAM, 0); if(socketListen < 0){ printf("创建TCP套接字失败\n"); exit(-1); }else{ printf("创建套接字成功\n"); } /* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */ struct sockaddr_in server_addr; bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */ server_addr.sin_port=htons(2000); if(bind(socketListen, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) != 0){ perror("绑定ip地址、端口号失败\n"); exit(-1); }else{ printf("绑定ip地址,端口号\n"); } /* 开始监听相应的端口 */ if(listen(socketListen, 10) != 0){ printf("开启监听失败\n"); exit(-1); }else{ printf("开启监听成功\n"); } /* 接受连接套接字 */ pthread_t thrAccept; pthread_create(&thrAccept,NULL,fun_thrAcceptHandler,&socketListen); /* 实时发送数据 */ while(1){ //判断线程存活多少 int i; for(i=0;i<thrReceiveClientCount;i++){ if(checkThrIsKill(arrThrReceiveClient[i]) == 1){ printf("有个线程被杀了\n"); thrReceiveClientCount--; } } printf("当前有接受数据线程多少个:%d\n",thrReceiveClientCount); // 可以录入用户操作选项,并进行相应操作 char userStr[30] = {'0'}; scanf("%s",userStr); if(strcmp(userStr,"q") == 0){ printf("用户选择退出!\n"); break; } // 发送消息 if(conClientCount <= 0){ printf("没有客户端连接\n"); }else{ int i; for(i=0; i<conClientCount; i++){ //int sendMsg_len = send(arrConSocket[i].socketCon, userStr, 30, 0); int sendMsg_len = write(arrConSocket[i].socketCon,userStr,30); if(sendMsg_len > 0){ printf("向%s:%d发送成功\n",arrConSocket[i].ipaddr,arrConSocket[i].port); }else{ printf("向%s:%d发送失败\n",arrConSocket[i].ipaddr,arrConSocket[i].port); } } } sleep(0.5); } // 等待子进程退出 printf("等待子线程退出,即将退出!\n"); char *message; pthread_join(thrAccept,(void *)&message); printf("%s\n",message); return 0;}void *fun_thrReceiveHandler(void *socketCon){ char buffer[30]; int buffer_length; int _socketCon; while(1){ //添加对buffer清零 bzero(&buffer,sizeof(buffer)); _socketCon = *((int *)socketCon); sleep(0.1); printf("接受数据线程中,连接套接字为%d\n",_socketCon); buffer_length = read(_socketCon,buffer,30); if(buffer_length == 0){ printf("%d 客户端关闭\n",_socketCon); conClientCount--; break; }else if(buffer_length < 0){ printf("接受客户端数据失败\n"); break; } buffer[buffer_length] = '\0'; printf("%d 说:%s\n",_socketCon,buffer); //获取当前线程id //printf("当前线程id:%ld",pthread_self()); sleep(0.2); } printf("接受数据线程结束了\n"); return NULL;}void *fun_thrAcceptHandler(void *socketListen){ while(1){ int sockaddr_in_size = sizeof(struct sockaddr_in); struct sockaddr_in client_addr; int _socketListen = *((int *)socketListen); int socketCon = accept(_socketListen, (struct sockaddr *)(&client_addr), (socklen_t *)(&sockaddr_in_size)); if(socketCon < 0){ printf("连接失败\n"); }else{ printf("连接成功 ip: %s:%d\r\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port); } printf("连接套接字为:%d\n",socketCon); //开启新的通讯线程,负责同连接上来的客户端进行通讯 _MySocketInfo socketInfo; socketInfo.socketCon = socketCon; socketInfo.ipaddr = inet_ntoa(client_addr.sin_addr); socketInfo.port = client_addr.sin_port; arrConSocket[conClientCount] = socketInfo; conClientCount++; printf("连接了%d个用户\n",conClientCount); pthread_t thrReceive = 0; pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon); arrThrReceiveClient[thrReceiveClientCount] = thrReceive; thrReceiveClientCount++; //让进程休息1秒 sleep(0.5); } char *s = "安全退出接受进程"; pthread_exit(s);}int checkThrIsKill(pthread_t thr){ int res = 1; int res_kill = pthread_kill(thr,0); if(res_kill == 0){ res = 0; } return res;}
客户端
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <string.h>#include <arpa/inet.h>#include <unistd.h>#include <pthread.h>typedef struct MySocketInfo{ int socketCon; unsigned long ipaddr; unsigned short port;}_MySocketInfo;void *fun_thrReceiveHandler(void *socketCon);int main(){ printf("开始socket\n"); /* 创建TCP连接的Socket套接字 */ int socketCon = socket(AF_INET, SOCK_STREAM, 0); if(socketCon < 0){ printf("创建TCP连接套接字失败\n"); exit(-1); } /* 填充客户端端口地址信息,以便下面使用此地址和端口监听 */ struct sockaddr_in server_addr; bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); server_addr.sin_port=htons(2000); printf("连接之前的socketCon:%d",socketCon); /* 连接服务器 */ int res_con = connect(socketCon,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)); if(res_con != 0){ printf("连接失败\n"); exit(-1); } printf("连接成功\n"); //开启新的实时接受数据线程 pthread_t thrReceive; pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon); /* 实时发送数据 */ while(1){ //char userStr[30] = {'\0'}; char *userStr = "woaini"; // 可以录入用户操作选项,并进行相应操作 //scanf("%s",userStr); //if(strcmp(userStr,"q") == 0){ //printf("用户选择退出!\n"); //break; //} // 发送消息 //int sendMsg_len = send(socketCon, userStr, 30, 0); printf("-发送之前-\n"); int sendMsg_len = write(socketCon,userStr,30); if(sendMsg_len > 0){ printf("发送成功,客户端套接字:%d\n",socketCon); }else{ printf("发送失败\n"); } sleep(2); } // 关闭套接字 close(socketCon); return 0;}void *fun_thrReceiveHandler(void *socketCon){ while(1){ char buffer[30]; int _socketCon = *((int *)socketCon); //int buffer_length = recv(_socketCon,buffer,30,0); int buffer_length = read(_socketCon,buffer,30); buffer[buffer_length] = '\0'; printf("服务器说:%s\n",buffer); } return NULL;}
代码思路:服务器端:首先创建监听的套接字,然后创建用于(循环检测客户端连接)的新线程,当检测到有客户端连接,则创建一个新的线程负责同这个套接字通讯,即接受这个套接字发送过来的消息。
现象:单个客户端同服务器可以进行通讯,当连接上多个客户端过后,服务器端能够群发消息,最后连接的客户端向服务器发送的消息能够接受到,但之前的所有的客户端向服务器发送消息均不能收到。
解决思路:先使用排除法,首先找到了一份正确的客户端代码,连接上我自己写的服务器,也出现了同样的错误,所以可以排除我自己写的客户端。最终到了服务器端。服务器中,因为单个客户端可以与服务器通讯,所以检测连接的线程没有问题,那么最终归结到了循环接受客户端数据的线程。那着正确的服务器接受客户端数据的代码同自己写的对照,哈哈,发现了问题,是_socketCon值的问题,_socketCon的赋值是通过指针,然后打印这个值,发现这个值在不断变化,并且在检测连接的线程中定义的 int socketCon 值正是这个,而这个值是在 while死循环中不停的创建,不停的销毁!哇咔咔,真相终于大白与天下,功夫不负有心人啊,查了一天半的bug终于被我查出来了!
总结:
1、在死循环中切记不要定义变量,最好是在死循环之外定义变量,在死循环中使用!
2、在函数有死循环时,值只有一个的局部变量,就在死循环之上定义并初始化!就不要在死循环中赋值,只是去使用它就好了!
3、在创建多线程给多线程处理函数传参数中,切记不要传入生命周期较短的指针变量,传入值类型都好!
服务器与多个客户端通讯的正确代码如下:
服务器端
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <string.h>#include <signal.h>#include <arpa/inet.h>#include <unistd.h>#include <pthread.h>//服务器端void *fun_thrReceiveHandler(void *socketInfo);void *fun_thrAcceptHandler(void *socketListen);//1:是 0:否int checkThrIsKill(pthread_t thr);typedef struct MySocketInfo{ int socketCon; char *ipaddr; uint16_t port;}_MySocketInfo;// 客户端数组struct MySocketInfo arrConSocket[10];int conClientCount = 0;// 接受客户端线程列表pthread_t arrThrReceiveClient[10];int thrReceiveClientCount = 0;int main(){ //初始化全局变量 //memset(arrConSocket,0,sizeof(struct MySocketInfo)*10); printf("开始socket\n"); /* 创建TCP连接的Socket套接字 */ int socketListen = socket(AF_INET, SOCK_STREAM, 0); if(socketListen < 0){ printf("创建TCP套接字失败\n"); exit(-1); }else{ printf("创建套接字成功\n"); } /* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */ struct sockaddr_in server_addr; bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */ server_addr.sin_port=htons(2000); if(bind(socketListen, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) != 0){ perror("绑定ip地址、端口号失败\n"); exit(-1); }else{ printf("绑定ip地址,端口号\n"); } /* 开始监听相应的端口 */ if(listen(socketListen, 10) != 0){ printf("开启监听失败\n"); exit(-1); }else{ printf("开启监听成功\n"); } /* 接受连接套接字 */ pthread_t thrAccept; pthread_create(&thrAccept,NULL,fun_thrAcceptHandler,&socketListen); /* 实时发送数据 */ while(1){ //判断线程存活多少 int i; for(i=0;i<thrReceiveClientCount;i++){ if(checkThrIsKill(arrThrReceiveClient[i]) == 1){ printf("有个线程被杀了\n"); thrReceiveClientCount--; } } printf("当前有接受数据线程多少个:%d\n",thrReceiveClientCount); // 可以录入用户操作选项,并进行相应操作 char userStr[30] = {'0'}; scanf("%s",userStr); if(strcmp(userStr,"q") == 0){ printf("用户选择退出!\n"); break; } // 发送消息 if(conClientCount <= 0){ printf("没有客户端连接\n"); }else{ int i; for(i=0; i<conClientCount; i++){ //int sendMsg_len = send(arrConSocket[i].socketCon, userStr, 30, 0); int sendMsg_len = write(arrConSocket[i].socketCon,userStr,30); if(sendMsg_len > 0){ printf("向%s:%d发送成功\n",arrConSocket[i].ipaddr,arrConSocket[i].port); }else{ printf("向%s:%d发送失败\n",arrConSocket[i].ipaddr,arrConSocket[i].port); } } } sleep(0.5); } // 等待子进程退出 printf("等待子线程退出,即将退出!\n"); char *message; pthread_join(thrAccept,(void *)&message); printf("%s\n",message); return 0;}void *fun_thrAcceptHandler(void *socketListen){ while(1){ int sockaddr_in_size = sizeof(struct sockaddr_in); struct sockaddr_in client_addr; int _socketListen = *((int *)socketListen); int socketCon = accept(_socketListen, (struct sockaddr *)(&client_addr), (socklen_t *)(&sockaddr_in_size)); if(socketCon < 0){ printf("连接失败\n"); }else{ printf("连接成功 ip: %s:%d\r\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port); } printf("连接套接字为:%d\n",socketCon); //开启新的通讯线程,负责同连接上来的客户端进行通讯 _MySocketInfo socketInfo; socketInfo.socketCon = socketCon; socketInfo.ipaddr = inet_ntoa(client_addr.sin_addr); socketInfo.port = client_addr.sin_port; arrConSocket[conClientCount] = socketInfo; conClientCount++; printf("连接了%d个用户\n",conClientCount); pthread_t thrReceive = 0; pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketInfo); arrThrReceiveClient[thrReceiveClientCount] = thrReceive; thrReceiveClientCount++; //让进程休息1秒 sleep(0.5); } char *s = "安全退出接受进程"; pthread_exit(s);}void *fun_thrReceiveHandler(void *socketInfo){char buffer[30];int buffer_length;_MySocketInfo _socketInfo = *((_MySocketInfo *)socketInfo); while(1){ //添加对buffer清零 bzero(&buffer,sizeof(buffer)); buffer_length = read(_socketInfo.socketCon,buffer,30); if(buffer_length == 0){ printf("%s:%d 客户端关闭\n",_socketInfo.ipaddr,_socketInfo.port); conClientCount--; break; }else if(buffer_length < 0){ printf("接受客户端数据失败\n"); break; } buffer[buffer_length] = '\0'; printf("%s:%d 说:%s\n",_socketInfo.ipaddr,_socketInfo.port,buffer); sleep(0.2); } printf("接受数据线程结束了\n"); return NULL;}int checkThrIsKill(pthread_t thr){ int res = 1; int res_kill = pthread_kill(thr,0); if(res_kill == 0){ res = 0; } return res;}
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <string.h>#include <signal.h>#include <arpa/inet.h>#include <unistd.h>#include <pthread.h>//服务器端void *fun_thrReceiveHandler(void *socketInfo);void *fun_thrAcceptHandler(void *socketListen);//1:是 0:否int checkThrIsKill(pthread_t thr);typedef struct MySocketInfo{ int socketCon; char *ipaddr; uint16_t port;}_MySocketInfo;// 客户端数组struct MySocketInfo arrConSocket[10];int conClientCount = 0;// 接受客户端线程列表pthread_t arrThrReceiveClient[10];int thrReceiveClientCount = 0;int main(){ //初始化全局变量 //memset(arrConSocket,0,sizeof(struct MySocketInfo)*10); printf("开始socket\n"); /* 创建TCP连接的Socket套接字 */ int socketListen = socket(AF_INET, SOCK_STREAM, 0); if(socketListen < 0){ printf("创建TCP套接字失败\n"); exit(-1); }else{ printf("创建套接字成功\n"); } /* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */ struct sockaddr_in server_addr; bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */ server_addr.sin_port=htons(2000); if(bind(socketListen, (struct sockaddr *)&server_addr,sizeof(struct sockaddr)) != 0){ perror("绑定ip地址、端口号失败\n"); exit(-1); }else{ printf("绑定ip地址,端口号\n"); } /* 开始监听相应的端口 */ if(listen(socketListen, 10) != 0){ printf("开启监听失败\n"); exit(-1); }else{ printf("开启监听成功\n"); } /* 接受连接套接字 */ pthread_t thrAccept; pthread_create(&thrAccept,NULL,fun_thrAcceptHandler,&socketListen); /* 实时发送数据 */ while(1){ //判断线程存活多少 int i; for(i=0;i<thrReceiveClientCount;i++){ if(checkThrIsKill(arrThrReceiveClient[i]) == 1){ printf("有个线程被杀了\n"); thrReceiveClientCount--; } } printf("当前有接受数据线程多少个:%d\n",thrReceiveClientCount); // 可以录入用户操作选项,并进行相应操作 char userStr[30] = {'0'}; scanf("%s",userStr); if(strcmp(userStr,"q") == 0){ printf("用户选择退出!\n"); break; } // 发送消息 if(conClientCount <= 0){ printf("没有客户端连接\n"); }else{ int i; for(i=0; i<conClientCount; i++){ //int sendMsg_len = send(arrConSocket[i].socketCon, userStr, 30, 0); int sendMsg_len = write(arrConSocket[i].socketCon,userStr,30); if(sendMsg_len > 0){ printf("向%s:%d发送成功\n",arrConSocket[i].ipaddr,arrConSocket[i].port); }else{ printf("向%s:%d发送失败\n",arrConSocket[i].ipaddr,arrConSocket[i].port); } } } sleep(0.5); } // 等待子进程退出 printf("等待子线程退出,即将退出!\n"); char *message; pthread_join(thrAccept,(void *)&message); printf("%s\n",message); return 0;}void *fun_thrAcceptHandler(void *socketListen){ while(1){ int sockaddr_in_size = sizeof(struct sockaddr_in); struct sockaddr_in client_addr; int _socketListen = *((int *)socketListen); int socketCon = accept(_socketListen, (struct sockaddr *)(&client_addr), (socklen_t *)(&sockaddr_in_size)); if(socketCon < 0){ printf("连接失败\n"); }else{ printf("连接成功 ip: %s:%d\r\n",inet_ntoa(client_addr.sin_addr),client_addr.sin_port); } printf("连接套接字为:%d\n",socketCon); //开启新的通讯线程,负责同连接上来的客户端进行通讯 _MySocketInfo socketInfo; socketInfo.socketCon = socketCon; socketInfo.ipaddr = inet_ntoa(client_addr.sin_addr); socketInfo.port = client_addr.sin_port; arrConSocket[conClientCount] = socketInfo; conClientCount++; printf("连接了%d个用户\n",conClientCount); pthread_t thrReceive = 0; pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketInfo); arrThrReceiveClient[thrReceiveClientCount] = thrReceive; thrReceiveClientCount++; //让进程休息1秒 sleep(0.5); } char *s = "安全退出接受进程"; pthread_exit(s);}void *fun_thrReceiveHandler(void *socketInfo){char buffer[30];int buffer_length;_MySocketInfo _socketInfo = *((_MySocketInfo *)socketInfo); while(1){ //添加对buffer清零 bzero(&buffer,sizeof(buffer)); buffer_length = read(_socketInfo.socketCon,buffer,30); if(buffer_length == 0){ printf("%s:%d 客户端关闭\n",_socketInfo.ipaddr,_socketInfo.port); conClientCount--; break; }else if(buffer_length < 0){ printf("接受客户端数据失败\n"); break; } buffer[buffer_length] = '\0'; printf("%s:%d 说:%s\n",_socketInfo.ipaddr,_socketInfo.port,buffer); sleep(0.2); } printf("接受数据线程结束了\n"); return NULL;}int checkThrIsKill(pthread_t thr){ int res = 1; int res_kill = pthread_kill(thr,0); if(res_kill == 0){ res = 0; } return res;}
客户端
#include <stdlib.h>#include <sys/types.h>#include <stdio.h>#include <sys/socket.h>#include <string.h>#include <arpa/inet.h>#include <unistd.h>#include <pthread.h>typedef struct MySocketInfo{ int socketCon; unsigned long ipaddr; unsigned short port;}_MySocketInfo;void *fun_thrReceiveHandler(void *socketCon);int checkThrIsKill(pthread_t thr);int main(){ printf("开始socket\n"); /* 创建TCP连接的Socket套接字 */ int socketCon = socket(AF_INET, SOCK_STREAM, 0); if(socketCon < 0){ printf("创建TCP连接套接字失败\n"); exit(-1); } /* 填充客户端端口地址信息,以便下面使用此地址和端口监听 */ struct sockaddr_in server_addr; bzero(&server_addr,sizeof(struct sockaddr_in)); server_addr.sin_family=AF_INET; server_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); /* 这里地址使用全0,即所有 */ server_addr.sin_port=htons(2000); /* 连接服务器 */ int res_con = connect(socketCon,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)); if(res_con != 0){ printf("连接失败\n"); exit(-1); } printf("连接成功,连接结果为:%d\n",res_con); //开启新的实时接受数据线程 pthread_t thrReceive; pthread_create(&thrReceive,NULL,fun_thrReceiveHandler,&socketCon); /* 实时发送数据 */ while(1){ //检测接受服务器数据线程是否被杀死 char userStr[30] = {'0'}; // 可以录入用户操作选项,并进行相应操作 scanf("%s",userStr); if(strcmp(userStr,"q") == 0){ printf("用户选择退出!\n"); break; } // 发送消息 //int sendMsg_len = send(socketCon, userStr, 30, 0); int sendMsg_len = write(socketCon,userStr,30); if(sendMsg_len > 0){ printf("发送成功,服务端套接字句柄:%d\n",socketCon); }else{ printf("发送失败\n"); } //if(checkThrIsKill(thrReceive) == 1){ //printf("接受服务器数据的线程已被关闭,退出程序\n"); //break; //} } // 关闭套接字 close(socketCon); return 0;}void *fun_thrReceiveHandler(void *socketCon){ while(1){ char buffer[30]; int _socketCon = *((int *)socketCon); //int buffer_length = recv(_socketCon,buffer,30,0); int buffer_length = read(_socketCon,buffer,30); if(buffer_length == 0){ printf("服务器端异常关闭\n"); exit(-1); }else if(buffer_length < 0){ printf("接受客户端数据失败\n"); break; } buffer[buffer_length] = '\0'; printf("服务器说:%s\n",buffer); } printf("退出接受服务器数据线程\n"); return NULL;}int checkThrIsKill(pthread_t thr){ int res = 1; int res_kill = pthread_kill(thr,0); if(res_kill == 0){ res = 0; } return res;}
1 0
- Linux C利用Socket套接字进行服务器与多个客户端进行通讯
- Linux C利用Socket套接字进行服务器与多个客户端进行通讯
- Linux C 服务端同时与多个客户端进行通讯
- C# Sockets实现服务器与多个客户端进行通讯
- java Socket 一个服务器与多个客户端进行通信
- 安卓客户端通过socket与服务器进行通讯
- Socket套接字:客户端与服务端进行通信
- 使用Socket进行一台服务器与多个Android客户端进行双向通信
- 使用Socket进行一台服务器与多个Android客户端进行双向通信
- 服务器如何跟多个客户端进行通讯呢?
- 使用hessian进行服务器与客户端通讯
- Android客户端,服务器,如何进行socket通讯的?
- 基于linux poll模型的tcp服务器------一个服务器如何与多个客户端进行通信?
- java--通过socket和多线程进行多个客服端与服务器的简单通讯--基于tcp
- 非阻塞socket对应的多线程服务器的实现---一个服务器如何与多个客户端进行通信?
- 利用AMF数据封装与Flash 进行Socket通讯
- Java 利用套接字Socket实现简单的服务器与客户端通信
- 采用json格式进行服务器与android客户端的通讯
- servlet线程安全
- css 权重
- android WebView登录状态session id 和cookie同步
- 【前端福利】用grunt搭建自动化的web前端开发环境-完整教程
- 如何下载之前版本的xcode
- Linux C利用Socket套接字进行服务器与多个客户端进行通讯
- Android Studio git环境配置
- DOM中为事件添加事件处理程序的几种方式以及跨浏览器方式
- HDOJ 2159 FATE
- android send key event
- Read the Last line in a file
- html form
- 国内Android应用分析报告,看看大家都在用什么开发
- CentOS安装JDK1.7