设计思路
服务端
用一个数组来存放连接的客户端的sockid,一个COUNT来存放当前连接的客户端数量。服务端listen后一直处在accept状态,每接收到一个连接,就创建一个线程来完成与客户端的通信。当一个客户端发言时就遍历存放sockid的数组,向其它所有用户发送这条信息。
客户端
连接服务端后启动一个线程专门接收服务端返回的信息,主线程专门负责向服务端发送消息。
/*头文件*/
#ifndef _PUBLIC_H
#define _PUBLIC_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#endif
/*server.c*/
#include "public.h"
#include <signal.h>
#define PORT 8000
#define MAX 20
int sock_list[MAX], COUNT = 0;
pthread_mutex_t mutex_lock;
void sent(const int sockid,const char *name, const char *str)
{
printf("sending..\n");
for (int i = 0; i < COUNT; i++)
{
/* code */
if(sock_list[i] != sockid)
{
write(sock_list[i], name, sizeof(name));
sleep(0.5);
write(sock_list[i], str, sizeof(str));
//printf("%s %s\n", name, str);
}
}
}
void delete(int sockid)
{
for (int i = 0; i < COUNT; i++)
{
if(sock_list[i] == sockid)
{
close(sockid);
for (int j = i; i < COUNT - 1; j++)
{
sock_list[j] = sock_list[j+1];
}
COUNT--;
break;
}
}
}
void *pth_rec(void *cli_sockfd)
{
int sockfd = *((int *)cli_sockfd);
char buff[100];
char name[20];
char LOGIN[100] = "login";
char QUIT[100] = "quit";
int res = 1;
memset(name,0,sizeof(name));
read(sockfd, name, sizeof(name));
pthread_mutex_lock(&mutex_lock);
sent(sockfd, name, LOGIN);
pthread_mutex_unlock(&mutex_lock);
while(res)
{
memset(buff,0,sizeof(buff));
res = read(sockfd, buff, sizeof(buff));
if(res < 0)
{
perror("read failed");
exit(-1);
}
else if (res == 0)
{
pthread_mutex_lock(&mutex_lock);
printf("client clsoe\n");
sent(sockfd, name, QUIT);
delete(sockfd);
pthread_mutex_unlock(&mutex_lock);
pthread_exit(0);
}
else
{
pthread_mutex_lock(&mutex_lock);
printf("%s\n", buff);
sent(sockfd, name, buff);
pthread_mutex_unlock(&mutex_lock);
}
}
}
int main()
{
int sockfd;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket failed");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
inet_aton("127.0.0.1", &addr.sin_addr);
int on=1;
if((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
{
perror("setsockopt failed");
exit(-1);
}
if(bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
{
perror("bind failed");
exit(-1);
}
if(listen(sockfd, MAX) == -1)
{
perror("listen failed");
exit(-1);
}
printf("listen..\n");
pthread_t tid;
pthread_mutex_init(&mutex_lock,NULL);
int cli_sockfd, ret;
struct sockaddr_in cli_addr;
socklen_t len = sizeof(cli_addr);
while(1)
{
cli_sockfd = accept(sockfd, (struct sockaddr *)&cli_addr, &len);
if(cli_sockfd == -1)
{
perror("accept failed");
exit(-1);
}
printf("%s connect\n", inet_ntoa(cli_addr.sin_addr));
sock_list[COUNT++] = cli_sockfd;
ret = pthread_create(&tid, NULL, pth_rec, &cli_sockfd);
if(ret != 0)
{
perror("pthread failed");
exit(-1);
}
}
return 0;
}
/*client.c*/
#include "public.h"
#define PORT 8000
int sockfd;
void * pth_rec()
{
char name[20];
char buff[100];
while(1)
{
memset(name, 0, sizeof(name));
memset(buff, 0, sizeof(buff));
read(sockfd, name, sizeof(name));
//printf("%s\n", name);
read(sockfd, buff, sizeof(buff));
printf("%s:%s\n", name, buff);
}
}
int main()
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd == -1)
{
perror("socket failed");
exit(-1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
inet_aton("127.0.0.1", &addr.sin_addr);
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("connect failed");
exit(-1);
}
printf("connect success\n");
pthread_t t_id;
char name[20];
printf("Input your name\n");
scanf("%s", name);
getchar();
if(write(sockfd, name, strlen(name)) < 0)
{
perror("read failed");
exit(-1);
}
pthread_create(&t_id, NULL, pth_rec, NULL);
while(1)
{
char buff[100];
scanf("%s", buff);
if(strcmp(buff, "quit") == 0)
{
break;
}
if(write(sockfd, buff, strlen(buff)) < 0)
{
perror("read failed");
exit(-1);
}
}
close(sockfd);
return 0;
}