#include
#include
#include
#include
#include
#include
#include
#define PORT 2233 //端口号
#define MAX_CLI 32 //系统能容纳的客户数
/*记录客户信息*/
struct client_info{
char msg[1024];
char c_name[64];//客户名字
char s_name[64];//私聊客户名字
int fd;//新连接描述符
struct sockaddr_in client;//其IP地址
};
/*记录客户发来信息*/
struct client_recvinfo{
char msg[1024];
char c_name[64];//客户名字
char s_name[64];//私聊客户名字
};
struct client_info bak_cli[MAX_CLI];//备份用户信息
/*将发送过来的信息保存*/
struct client_info copy_recv(struct client_info bak,const struct client_recvinfo new){
strncpy(bak.msg,new.msg, strlen(new.msg));
strncpy(bak.c_name,new.c_name, strlen(new.c_name));
strncpy(bak.s_name,new.s_name, strlen(new.s_name));
return bak;
}
/*打包要发送的信息*/
void buf_send(char* buf,const struct client_recvinfo new){
if(strcmp(new.s_name,"all")==0){
strcat(buf,"\033[31m");
strcat(buf,new.c_name);
strcat(buf,"\033[0m");
strcat(buf,"(群发)");
strcat(buf,": ");
strcat(buf,new.msg);
//printf("\n==pu==\n");
}
else{
strcat(buf,"\033[31m");
strcat(buf,new.c_name);
strcat(buf,"\033[0m");
strcat(buf,"(私发)");
strcat(buf,": ");
strcat(buf,new.msg);
//printf("\n==pr==\n");
}
}
//向所有在线用户发送在线的用户列表
void send_oname(){
char on_names[1024];
long ret;
//记录在线的用户列表
memset(&on_names,0, sizeof(on_names));
strcat(on_names,"当前在线用户: ");
for(int i =0;i<MAX_CLI;i++){
if(bak_cli[i].fd!=0){
strcat(on_names,"[");
strcat(on_names,bak_cli[i].c_name);
strcat(on_names,"] ");
}
}
//printf("=%s=\n",on_names);
//向所有在线用户发送
for(int i = 0;i<MAX_CLI;i++){
if(bak_cli[i].fd!=0) {
ret = send(bak_cli[i].fd, &on_names, strlen(on_names), 0);
if (ret < 0) {
perror("send name");
}
}
}
}
/*初始化服务器*/
int init_tcp_server(unsigned short port){
int listen_fd;
int ret;
int opt;
struct sockaddr_in self;
//监听描述符
listen_fd = socket(AF_INET,SOCK_STREAM,0);
if(listen_fd < 0){
perror("socket");
return -1;
}
// 配置监听描述符地址复用属性
opt = 1;
ret = setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR,&opt, sizeof(opt));
if (ret < 0) {
perror("set socket opt");
return -1;
}
// 填充服务器开放接口和端口号信息
memset(&self,0, sizeof(self));
self.sin_family = AF_INET;
self.sin_port = htons(port);
self.sin_addr.s_addr = htonl(INADDR_ANY);
//将self与listen_fd绑定
ret = bind(listen_fd,(struct sockaddr *)&self, sizeof(self));
if(ret<0){
perror("bind");
return -1;
}
// 默认socket是双向,配置成监听模式
listen(listen_fd, 5);
return listen_fd;
}
/*与客户端建立连接,并保存登陆信息*/
struct client_info listen_hander(int fd){
long ret;
struct client_info new;
struct client_recvinfo name;
memset(&new,0, sizeof(new));
unsigned int len = sizeof(new.client);
//监听描述符
new.fd= accept(fd,(struct sockaddr *) &(new.client),&len);
if(new.fd<0){
perror("accept");
}
//接收新的用户名
ret = recv(new.fd,&name, sizeof(name),0);
if(ret<0){
perror("recv name");
}
//检测是否同名
for(int i=0;i<MAX_CLI;i++) {
if (strcmp(bak_cli[i].c_name, name.c_name) == 0) {
ret = send(new.fd, "repetitive name", 15, 0);
if (ret < 0) {
perror("send repetitive name");
}
ret = -1;//用于下个循环判断是否重名
close(new.fd);
memset(&new, 0, sizeof(new));
memset(&name, 0, sizeof(name));
break;
}
}
//保存新连接用户信息
for(int i=0;i<MAX_CLI;i++) {
if(ret < 0){
break;
}
else if(bak_cli[i].fd==0){
bak_cli[i] = copy_recv(new,name);
//每上线一个用户就更新在线用户列表
send_oname();
printf("\033[32m%s\033[0m %s:%d \033[32m online\033[0m\n",name.c_name,
inet_ntoa(new.client.sin_addr), ntohs(new.client.sin_port));
break;
}
}
return new;
}
/*接收客户端数据并处理*/
int data_hander(int new_fd){
struct client_recvinfo new;
long ret;
char buf[2048];
int w_name = 1; //判断私发用户是否存在
memset(&new,0, sizeof(new));
ret = recv(new_fd, &new, sizeof(new), 0);
if (ret < 0) {
perror("recv");
return -1;
} else if (ret == 0) {
//清理退出的用户
for(int i =0;i<MAX_CLI;i++){
if(bak_cli[i].fd==new_fd) {
printf("\033[32m%s\033[0m %s:%d \033[32m offline\033[0m\n",
bak_cli[i].c_name,
inet_ntoa(bak_cli[i].client.sin_addr),
ntohs(bak_cli[i].client.sin_port));
memset(&bak_cli[i], 0, sizeof(bak_cli[i]));
//每下线一个用户就更新在线用户列表
send_oname();
}
}
return 0;
}
else {
// 打印消息
//找到当前用户的备份信息
for (int j = 0; j < MAX_CLI; j++) {
if (bak_cli[j].fd == new_fd) {
bak_cli[j] = copy_recv(bak_cli[j], new);
}
}
//分析接收的消息并处理
for (int i = 31; i >= 0; i--) {
//群发
if (strcmp(new.s_name,"all")==0) {
if (new_fd != bak_cli[i].fd&&bak_cli[i].fd != 0) {
memset(&buf,0, sizeof(buf));
buf_send(buf, new);
ret = send(bak_cli[i].fd, buf, strlen(buf), 0);
if (ret < 0) {
perror("public send");
}
printf(" (群)%s send %s to %s\n",
new.c_name, new.msg, bak_cli[i].c_name);
w_name = 0;
}
}
//私发
else if (strcmp(new.s_name,
bak_cli[i].c_name) == 0) {
memset(&buf,0, sizeof(buf));
buf_send(buf, new);
ret = send(bak_cli[i].fd, buf, strlen(buf), 0);
if (ret < 0) {
perror("private send");
}
printf(" (私)%s send %s to %s\n",
new.c_name, new.msg, bak_cli[i].c_name);
w_name = 0;
break;
}
}
if(w_name){
ret = send(new_fd,"404",3, 0);
if (ret < 0) {
perror("send 404");
}
printf("%s send %s to %s failed!\n",
new.c_name, new.msg, new.s_name);
}
//回应收到了消息
// ret = send(new_fd,"server: msg recv",16,0);
// if(ret < 0){
// perror("answer");
// }
}
return (int)ret;
}
void main_loop(int listen_fd){
fd_set set,bak_set;
int max_fds;
int ret;
struct client_info new;
// 把监听描述符、标准输入描述符添加到集合
FD_ZERO(&set);
FD_SET(listen_fd,&set);
FD_SET(0,&set);
max_fds = listen_fd;
while(1){
bak_set = set;
ret = select(max_fds+1,&bak_set,NULL,NULL,NULL);
if(ret < 0){
perror("select");
break;
}
for(int i = 0;i<=max_fds;i++){
if(FD_ISSET(i,&bak_set)){
if(i == 0){
// 标准输入可读 fgets
continue;
}
else if(i == listen_fd){
// 监听描述符可读 accept
memset(&new,0, sizeof(new));
new = listen_hander(i);
if(new.fd<0||new.fd==0){
fprintf(stderr, "listen handler error!\n");
continue;
}
FD_SET(new.fd, &set);
max_fds = new.fd > max_fds ? new.fd : max_fds;
}
else{
// 新的连接描述符可读 recv
ret = data_hander(i);
if (ret <= 0) {
// 收尾处理
close(i);
FD_CLR(i, &set);
}
}
}
}
}
}
int main(){
int listen_fd;
listen_fd = init_tcp_server(PORT);
if (listen_fd < 0) {
fprintf(stderr, "init tcp server failed!\n");
return -1;
}
printf("listening...\n");
main_loop(listen_fd);
close(listen_fd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
static short PORT = 2233;
static char *HOST = "192.168.200.134";
/*打包要发送的信息*/
struct send_info{
char msg[1024];
char c_name[64];//客户名字
char s_name[64];//私聊客户名字
};
static struct send_info msg;
static char cname[64];
/*初始化*/
int inti_tcp_client(const char *host,unsigned short port){
int scoket_server;
int ret;
struct sockaddr_in dest;
scoket_server = socket(AF_INET, SOCK_STREAM, 0);
if(scoket_server < 0){
perror("socket");
return -1;
}
memset(&dest,0, sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(port);
dest.sin_addr.s_addr = inet_addr(host);
ret = connect(scoket_server, (struct sockaddr *)&dest, sizeof(dest));
if(ret < 0){
perror("connect");
return -1;
}
return scoket_server;
}
/*记录用户名称,并接收服务器在线的用户*/
int set_name(int scoket_server) {
char buf[1024];
long ret;
// 保存登陆名并发给服务器
memset(&msg,0, sizeof(msg));
strcat(msg.c_name,cname);
ret = send(scoket_server,&msg, sizeof(msg),0);
if(ret<0){
perror("send");
return -1;
}
//打印连接信息
memset(&buf,0, sizeof(buf));
ret = recv(scoket_server, &buf, sizeof(buf)-1, 0);
//buf[strlen(buf)] = 0;
if (ret > 0) {
if(strncmp(buf,"repetitive name\n",15)==0){
printf("名称重复!\n");
exit(0);
}
else {
printf("成功连接到主机: %d!\t", PORT);
printf("%s\n", buf); //打印在线的用户
}
}
return 0;
}
/*接收服务器的消息*/
void recv_hander(int client_fd){
char buf[1024];
long ret;
memset(buf,0, sizeof(buf));
while (1){
ret = recv(client_fd, &buf, sizeof(buf)-1, 0);
if (ret < 0) {
perror("recv hander");
break;
}
else if(ret == 0){
printf("服务器崩了!!!!\n");
break;
}
if(strncmp(buf,"404",3)==0){
printf("\033[31m服务器\033[0m: 找不到该用户!\n");
}
else{
printf("%s\n", buf);
}
memset(buf,0, sizeof(buf));
}
}
/*打包消息并发给服务器*/
void send_hander(int client_fd){
int type;
long ret;
while(1){
usleep(1000);
printf("请输入聊天方式(1 群聊,0私聊):\n");
scanf("%d",&type);
fgets(msg.msg, sizeof(msg.msg), stdin);//清除stdin的内容
if(type){
printf("请输入聊天信息:\n");
memset(&msg,0, sizeof(msg));
fgets(msg.msg, sizeof(msg.msg), stdin);//要发送的信息
msg.msg[strlen(msg.msg)-1] = 0;
strcat(msg.s_name,"all");//要发送的对象
strcat(msg.c_name,cname);//用户姓名
}
else{
printf("请输入接收方姓名:\n");
memset(&msg,0, sizeof(msg));
fgets(msg.s_name, sizeof(msg.s_name), stdin);
msg.s_name[strlen(msg.s_name)-1] = 0;
printf("请输入聊天信息:\n");
fgets(msg.msg, sizeof(msg.msg), stdin);
msg.msg[strlen(msg.msg)-1] = 0;
strcat(msg.c_name,cname);
}
ret = send(client_fd,&msg, sizeof(msg),0);
if(ret<0){
perror("send");
break;
}
}
}
int main(){
int client_socket;
//记录登陆名
printf("请输入你的名字:\t");
fgets(cname, sizeof(cname), stdin);
cname[strlen(cname)-1]=0;
client_socket = inti_tcp_client(HOST,PORT);
if (client_socket < 0) {
fprintf(stderr, "init tcp client failed!\n");
return -1;
}
set_name(client_socket);
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork");
} else if (pid == 0) {
recv_hander(client_socket);
close(client_socket);
exit(0);
} else {
send_hander(client_socket);
close(client_socket);
kill(pid,9); //关闭接收子进程,准备退出
wait(NULL);
}
return 0;
}
之前做的,没有优化,写的有点混乱,但基本实现了群聊和私聊