您的位置:首页 > 运维架构 > Linux

2.4 linux利用Socket套接字一个服务器向多个客户端群发消息(实验二)

2018-01-19 17:15 399 查看
-------------------下面代码来源于网上,本人仅测试和修改,并未做其他改变

     在测试过程中发现,记录如下:

1.客户端初始化 一次Socket 连上服务器,如果此时连不上,就要重启才能连上(待优化) 
2.客户端发数据给服务器, 命令行 显示的数据不完整,收到消息凌乱(待优化) 
  
     这个是可以用的,亲测成功,很高兴! 

============================================================================
                                                                                             代码如下
                                                      功能:一个服务器连接多个客户端,向客户端群发消息

============================================================================
服务器端代码如下:

#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#
4000
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(5566);
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.    2个客户端接入

         虚拟机和网络调试助手软件  接入 服务器端 如下:



2.  服务器群发消息

  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐