服务端:
#include <pthread.h>
#include<stdio.h>
#include<WinSock2.h>//调用套接字的头函数
#include <stdlib.h>
#define maxsize 20 //最大客户量
int cIntSock [maxsize]={} ;//用来存放客户
//群发函数
void *sendall(char *buf,int *fd)
{
int k=*(int*)fd; //将fd强制转换为int型指针 ,然后将其值赋给K
int i;
for(i=0;i<maxsize;i++)
{
if(-1!=cIntSock[i]&&k!=cIntSock[i])//若有客户 且不为发送消息本人 !!!现在客户端自己的消息不会发给自己了!!!!
{
send(cIntSock[i], buf, strlen(buf) + 1, 0);//则发送信息
}
}
printf("群发命令执行成功!\n");
printf("\n");
}
//服务端接收函数
void *server(void *a)
{
int fd=*(int*)a; //将指针a强制转换为整型指针 (类似于int *a),然后将其值赋给fd(即fd=*a) ,所以fd就是一个变量罢了
printf("accept返回值:%d\n",fd);//打印fd的值出来看看,其根本是accept的返回值
char buf[maxsize];//存储消息
char name[maxsize]={"未知客户"};//存储ID ,需要先赋值,否则客户未注册直接关闭了客户端,会有小错误解
char ts[maxsize];//存放需要群发的信息
//故障记录,若客户端连接后正常注册登录,就执行下面
int l=recv(fd,name,sizeof(name),0);
if(l>0)
{
printf("%s进入群聊\n",name);
printf("\n");
sprintf(ts,"%s进入群聊!!!\n",name);//把数据写到ts里面
printf("执行群发命令\n");
sendall(ts,&fd);//进群消息群发给所有客户端
while (1) {
int m;
m=recv(fd, buf, sizeof(buf), 0);//第一个参数为建立的通信(客户端发来的信息),将其复制到数组中(第二个类似于缓存区存放来自客户端的数据)
//找到退出群聊的人,置为-1 ,或者有人断开连接
if(m<=0||strcmp(buf,"quit")==0)
{
sprintf(ts,"%s离开群聊\n",name);
int index=0;
for(;index<maxsize;index++)
{
if(cIntSock[index]==fd)
{
cIntSock[index]=-1;
break;
}
}
printf("执行群发命令\n");
sendall(ts,&fd);//群发退群消息
printf("%s退出群聊,已关闭线程\n",name);//在服务端显示
printf("\n");
strcpy(buf,"quit");
send(fd,buf,strlen(buf) + 1, 0);//发送quit给需要退出的客户端,使其成功退出
closesocket(fd);//关闭套接字
pthread_exit(NULL);//关闭线程
}
//若未退出群聊,则群发消息
sprintf(ts,"%s:%s\n",name, buf);
printf("执行群发命令\n");
sendall(ts,&fd);
}
}
else//客户端连接后不注册直接关闭,就关闭线程
{
printf("%s连接断开,已关闭线程\n",name);//在服务端显示
printf("\n");
closesocket(fd);//关闭套接字
pthread_exit(NULL);//关闭线程
}
}
//主函数
int main()
{
//初始化
WSADATA wsadata;//创建结构体变量
WSAStartup(MAKEWORD(2, 2), &wsadata);//初始化结构体变量
//创建套接字
printf("创建socket对象...\n");
SOCKET serverSock = socket(PF_INET, SOCK_STREAM, 0);//serversock是套接字变量名 (创建套接字)
if(0 > serverSock)
{
perror("socket");//失败则返回socket函数的错误原因,后面同理
return -1;
}
//指定通信地址 (网络类型地址)
printf("准备通信地址...\n");
struct sockaddr_in sockAddr;//创建网络类型地址的结构体变量
sockAddr.sin_family = PF_INET;//地址域,要与domain(socket的PF_INET)一致
sockAddr.sin_addr.s_addr =inet_addr("127.0.0.2"); //IP地址,inet_addr将点分十进制的IP地址字符串转换成网络字节序的4字节整数(sockAddr.sin_addr.s_addr是4字节网络字节序)
sockAddr.sin_port = htons(52000);//端口号 htons将地址字节序的2字节整数转换成网络字节序
//绑定地址
printf("绑定socket对象与地址...\n");
if(bind(serverSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)));//将通信地址与服务端套接字绑定
{
perror("bind");
}
//将套接字serversock与地址 (SOCKADDR*)&sockAddr绑定 ((SOCKADDR*)&sockAddr应为将sockaddr_in取地址后强制转换为sockaddr*(sockaddr指针)类型,因为地址定义的参数是sockaddr)
//监听端口
printf("设置监听...\n");
if(listen(serverSock, 20));//20指排队的数量
{
perror("listen");
}
//接收 客户端信息
printf("等待客户端链接...\n");
SOCKADDR cIntAddr; //sockaddr是基本地址 ,应该是用来接收客户端的地址
int nSize = sizeof(SOCKADDR);
//对用来存放客户的数组置为-1
int index;
for(index=0;index<maxsize;index++)
cIntSock[index]=-1;
printf("\n");
printf("* 执行功能界面 *\n");
for(; ;)
{
index=0;
while(cIntSock[index]!=-1) index++;//找到未被占领的位置
cIntSock[index]= accept(serverSock, (SOCKADDR*)&cIntAddr, &nSize);//cintsock就是用来接收客户端的信息的变量 ,accpt连接成功会返回一个值,赋给cintsock用来区别客户
perror("accept");
printf("\n");
printf("新客户端连接成功!已建立线程\n");
printf("\n");
//创立多线程
pthread_t tid;
pthread_create(&tid,NULL,server,&cIntSock[index]);//将客户发信息的套接字传给接收函数
}
//关闭创建的套接字
closesocket(serverSock);
WSACleanup();
return 0;
}
客户端:
#include<stdio.h>
#include<winsock2.h>
#include<pthread.h>
#define maxsize 20//宏定义
char F[maxsize]={"未命名"};//定义一个全局变量,在成功登陆后命名,便于判断登录与否
//存放账号密码
typedef struct seqqueue
{
char ID[maxsize][maxsize];
char password[maxsize][maxsize];
int len;
}seq;
//客户端接收函数
void *client_read(void*arg)
{
int fd=*(int*)arg;
char buf[50];//存放消息
for(; ;)
{
int x;
x=recv(fd, buf, 50, 0);//故障记录
if(x<=0||strcmp(buf,"quit")==0)//收到服务器发来的quit,或者被断开 结束线程
{
printf("已经与服务器断开连接\n");
pthread_exit(NULL);
}
//否则就正常输出
printf("%s\n",buf);
}
}
//注册函数
void register_1(seq *Q)
{
printf("请输入你想要的ID:\n");
Q->len=(Q->len+1);
scanf("%s",Q->ID[Q->len]);
printf("请设置你想要的密码:\n");
scanf("%s",Q->password[Q->len]);
printf("ID:\'%s\'注册成功!请牢记你的密码\n",Q->ID[Q->len]);
}
//注销函数
void cancel_1(seq *Q)
{
char a[maxsize];
printf("请输入你想要注销账号的ID:\n");
scanf("%s",a);
int z,y;
for( z=0;z<=Q->len;z++)//跟登录那一样,从0处开始,0处初始化了 ,否则会死循环!
{
if(strcmp(Q->ID[z],a)==0)//根据ID查找
{
y=z ;
}
else y=-1;
}
if(y!=-1)
{
int i;
for(i=y;i<=Q->len-1;i++)//删除队列中的信息
{
strcpy(Q->password[i],Q->password[i+1]);
strcpy(Q->ID[i],Q->ID[i+1]);
}
Q->len=Q->len-1;
printf("账号%s注销成功 \n",a);
}
else if(y==-1) printf("注销失败,未找到ID:%s\n",a);
}
//登录函数
int login_1(seq *Q)
{
char a[maxsize];
char b[maxsize];
printf("请输入ID:");
printf("\n");
scanf("%s",a);
printf("请输入password:");
printf("\n");
scanf("%s",b);
int x=0,y=0,z=0,h=0;
char q[maxsize];
for( x=0;x<=Q->len;x++)//从1开始找的话,1处未初始化,会出现直接登陆成功的错误
{
if(strcmp(Q->ID[x],a)==0)//根据ID查找
{
z=x;
}
else z=-1;
}
for( y=0;y<=Q->len;y++)
{
if(strcmp(Q->password[y],b)==0)//根据password查找
h=y;
}
else h=-1;
}
if(z!=-1&&h!=-1)
{
strcpy(F,a);
printf("登陆成功!\n");
return 1;
}
else if(z==-1)
{
printf("登录失败,此账号未注册,是否需要注册一个新账号?(请输入是/否)\n");
scanf("%s",q);
char s[maxsize];
strcpy(s,"是");
if(strcmp(q,s)==0)
{
printf("请输入你想要的ID:\n");
Q->len=(Q->len+1);
scanf("%s",Q->ID[Q->len]);
printf("请设置你想要的密码:\n");
scanf("%s",Q->password[Q->len]);
printf("ID:\'%s\'注册成功!请牢记你的密码\n",Q->ID[Q->len]);
printf("\n");
}
return -1;
}
else if(z!=-1&&h==-1)
{
printf("登录失败,密码错误\n");
return -1;
}
}
//退出函数
void exit_1()
{
printf("退出成功!\n");
exit(0);
}
//主函数
int main()
{
//初始化
WSADATA wsadata;
int nRes = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (nRes != 0) return -1;
//创建套接字
printf("创建socket对象...\n");
SOCKET sock = socket(PF_INET, SOCK_STREAM, 0);
if(0 > sock)
{
perror("socket");
return -1;
}
//通信地址
printf("准备通信地址(127.0.0.2)...\n");
struct sockaddr_in sockAddr;
sockAddr.sin_family = PF_INET;
sockAddr.sin_addr.s_addr = inet_addr("127.0.0.2"); //只需要在这里指向服务器 ip 就可以了
sockAddr.sin_port = htons(52000);//端口
//连接服务器
printf("链接服务端口52000...\n");
if(connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR)));
{
perror("connect");
}
printf("链接服务器成功!!!\n");
printf("\n");
//功能列表
int n;
seq Q;
Q.len=0;
strcpy(Q.ID[0],"NULL");//初始化
strcpy(Q.password[0],"NULL");
printf("* 功能列表 *\n");
printf("* 1. 注册; *\n");
printf("* 2. 注销; *\n");
printf("* 3. 登录; *\n");
printf("* 4. 退出。 *\n");
printf("*************************************************************************\n");
printf("\n");
while (1)
{
int op;
int p;
fflush(stdin);//需要清楚缓存,否则输入错误可能会死循环 (特别是输入字母时)
printf("请选择你要操作的选项:\n");
scanf("%d", &op);
switch (op)
{
case 1:
register_1(&Q);
printf("\n");
break;
case 2:
cancel_1(&Q);
printf("\n");
break;
case 3:
n=login_1(&Q);
if(n==1){
printf("\n");
//登陆成功后再建立线程,防止未登录就被服务器群发消息,不符合群聊逻辑
pthread_t tid;
pthread_create(&tid,NULL,client_read,&sock);
send(sock, F, strlen(F) + 1, 0);//发送ID给服务端
printf("* 聊天界面 *\n");
printf("欢迎进入群聊!!\n");
printf("(注意:群内禁止聊违法内容!)\n");
printf("(注意:输入quit退出群聊!)\n");
printf("\n");
while (1) {
char sendBuf[50]={"Hello"};
scanf("%s",sendBuf);
printf("\n");
p=send(sock, sendBuf, strlen(sendBuf) + 1, 0);
if(p<0||strcmp(sendBuf,"quit")==0)//输入quit和send发送失败时结束通信
{
printf("结束通信\n");
//关闭套接字,结束线程
closesocket(sock);
pthread_exit(NULL);
}
}
//关闭
WSACleanup();
system("pause");}
else break;
break;
case 4:
exit_1();
break;
default:
printf("error!\n");
break;
}
}
return 0;
}