能够实现简单的多人聊天和私聊
服务器接收来自客户端的连接请求,当有客户端发送过来数据时,服务器将数据保存到全局缓冲区,并将数据循环发送给已经连接的客户端
不解释直接上代码:
服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#define PORT 8081 //设置端口信息
#define BUFFER_SIZE 1024 //设置最大发送信息字节
#define MAX_SIZE 10 //最大连接数
struct Msg{ //发送消息的结构体包含姓名 发送的信息 该客户端的套接字描述符
char name[20];
char message[BUFFER_SIZE];
int socket;
int flge;
};
int connfd[MAX_SIZE]; //把连接进来的客户端放在数组中
void *recv_fun(void *arg); //接受信息函数
int main()
{
struct sockaddr_in sin,cin; //声明两个套接字
int sfd,cfd;
int Length = sizeof(struct sockaddr_in); //套接字结构体的大小
//char buf[BUFFER_SIZE];
int n;
if((sfd=socket(AF_INET,SOCK_STREAM,0))==-1){ //创建套接字
printf("socket error\n");
exit(1);
}
//为套接字设置ip协议 设置端口号 并自动获取本机ip转化为网络ip
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
sin.sin_addr.s_addr=htonl(INADDR_ANY);
bzero(&(sin.sin_zero),8);
if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0){ //绑定套接字
printf("bind error\n");
exit(1);
}
printf("Bind success!\n");
if(listen(sfd,MAX_SIZE)==-1){ //监听最大连接数
printf("listen error\n");
exit(1);
}
printf("listening....\n");
int i;
for(i=0; i<MAX_SIZE; i++) //刚开始先把存放客户端的数组设置初始值为-1
connfd[i] = -1;
printf("Weiting.......\n");
//等待客户端连接
while(1){
int i;
for(i=0; i<MAX_SIZE; i++){ //寻找到没有保存客户端的一个位置
if(connfd[i] == -1){
break;
}
}
//让该位置保存下一个连接进来的客户端
if((connfd[i]=accept(sfd, (struct sockaddr *)&cin, &Length)) == -1){//连接客户端
printf("accept error\n");
exit(1);
}else
printf("有人加入编号为:%d\n", i); //输入连接进来的客户端的编号
pthread_t th1;
pthread_create(&th1, NULL, recv_fun, (void*)&connfd[i]); // 创建一个线程把接受信息的函数加入到主函数中
}
return 0;
}
void *recv_fun(void *arg){
int fd = *((int *)arg); //用来保存刚才发消息过来的客户端的编号
struct Msg msg; //声明一个结构体用来接收发送过来的信息
int flge, num_send, num_recv;
while(1){
int n=recv(fd, (char*)&msg, sizeof(msg), 0); //接受发送过来的信息保存到msg的结构体中
if(n<=0){//判断是否接收成功
printf("recv error\n");
connfd[msg.socket] = -1;
pthread_exit(NULL);
exit(1);
}
int i;
for(i=0; i<MAX_SIZE; i++){ //如果客户端要私聊的话,先遍历数组找到要私聊的客户端的位置
if(fd == connfd[i])
break;
}
msg.socket = i;
//在服务器中显示一下发送的信息
printf("接受到编号为:%d,%s消息为:%s,\n\n\n", msg.socket, msg.name, msg.message);
if(msg.flge == -1){//-1时为群聊
for(i=0; i<MAX_SIZE; i++){//从开始位置一直找到最后一个存放客户端的位置除去自己其他的客户端都发送一边信息
if(connfd[i] != fd && connfd[i] != -1){
num_send=send(connfd[i], (char*)&msg, sizeof(msg), 0);
if(num_send < 0){
printf("send error\n");
}
}
}
}else{//如果不是群聊的话就把信息发送给指定位置的客户端上
num_send=send(connfd[msg.flge], (char*)&msg, sizeof(msg), 0);
if(num_send < 0 ){
printf("send error\n");
}
}
}
}
客户端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8081 //设置端口信息这是端口号要与服务器端口号相同
#define BUFFER_SIZE 1024 //设置最大发送信息字节
#define HOST_ADDR "127.0.0.1" //设置服务器的ip因为都是本地连接即服务器的端口为127.0.0.1
struct Msg{ //发送信息的结构体包含客户端的信息, 发送消息 套接字描述符 和要群发和私聊的判断符
char name[20];
char message[BUFFER_SIZE];
int socket;
int flge;
};
struct Msg msg;
void *send_fun(void *arg); //发送信息函数
void *recv_fun(void *arg); //接受信息函数
int main()
{
int sfd;
//char buf[BUFFER_SIZE];
struct sockaddr_in sin;
if((sfd=socket(AF_INET,SOCK_STREAM,0))==-1){//创建一个套接字AF_INET代表ipV4协议 SOCK_STREAM 是用TCP连接类型的有序字节流协议
printf("socket error\n");
exit(1);
}
//设置套接字地址信息AF_INET代表使用ipv4, htons(PORT)把本地端口转化为网络端口s代表转化为短型的 inte_pton函数就是把开始设置的ip转化为网络的ip并使用ipv4协议给套接字sin的ip
sin.sin_family=AF_INET;
sin.sin_port=htons(PORT);
inet_pton(AF_INET,HOST_ADDR,&sin.sin_addr);
/* if(inet_aton(HOST_ADDR,&sin.sin_addr)== -1)//与上面转化一样
{
printf("inet_aton error.\n");
return -1;
}
*/
bzero(&(sin.sin_zero),8);//为套接字补8个0
if(connect(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1)//连接客户端,sfd为套接字描述符, 第二个参数表示把sockaddr_in类型的sin转化为sockaddr类型的, 第三个参数为sin的字节数
{
printf("connet error\n");
close(sfd);
exit(1);
}
printf("输入姓名:");
scanf("%s", msg.name);
pthread_t th1, th2; //声明两个线程变量
//分别把send_fun函数和recv_fun函数加入到主函数中
if(pthread_create(&th1, NULL, send_fun, (void*)&sfd)){
perror("Create phtread error\n");
exit(1);
}
if(pthread_create(&th2, NULL, recv_fun, (void*)&sfd)){
perror("Create phtread error\n");
exit(1);
}
//等待线程结束
pthread_join(th1, NULL);
pthread_join(th2, NULL);
close(sfd);
return 0;
}
void *send_fun(void *arg){
int sfd= *((int *)arg);
while(1){
printf("请选择聊天方式:\n-1 群聊 \n输入其他客户端编号私聊\n请选择:\n");
scanf("%d", &msg.flge);//输入标记符后面用来判断是私聊还是群聊
printf("输入聊天内容:");
scanf("%s", msg.message);//聊天信息
int n = send(sfd, (char *)&msg, sizeof(msg), 0);//发送信息
if(n!= sizeof(msg)){
perror("send error\n");
exit(1);
}
}
}
void *recv_fun(void *arg){
int sfd=*((int*)arg);
struct Msg msg;
while(1){
int n=recv(sfd, (char*)&msg, sizeof(msg), 0);//接受信息函数
if(n == -1){
perror("recv error\n");
exit(1);
}
//输出接受的信息
printf("\n编号为:%d, 姓名为:%s, 发的信息为:%s\n\n", msg.socket, msg.name, msg.message);
}
}
服务器接收来自客户端的连接请求,当有客户端发送过来数据时,
服务器将数据保存到全局缓冲区,并将数据循环发送给已经连接的客户端