版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ReturningProdigal/article/details/51916754
服务器端
#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;
}