linux下用C语言写的聊天程序
要求:两个客户端可以进行聊天,服务器用来中转信息。我的主要思路是:对于每个客户端连接,服务器端建立子进程对其进行处理,注册消息格式:register aa bb(aa是用户名,bb是密码),服务器根据第一个字符串对其进行处理;登陆:login aa bb,登陆成功后可以进行chat、logout等后续操作,当收到chat dd hello时,便找到dd的socket地址和文件描述符,将消息转发给他。
可这时我发现消息根本无法转发,每个客户端连接时的new_fd都相同,消息只能回发给aa,这里我应该怎么实现呢,我看了好多程序,都是用线程,我想用子进程实现,希望大家提供点思路。。下面是我服务器端的代码
程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <error.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXDATASIZE 256
#define PORT 3000
#define BACKLOG 5
#define TM_L 14
struct users
{
char name[50];
char password[50];
char state[5];
char IP[20];
long port;
int fd;
};
typedef struct users user;
//定义查询函数
int selectdb(user **cl,int f)
{
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
if (length < i)
{
*cl == malloc(20);
puts("empty");
return -1;
}
if (*cl != NULL)
{
free(*cl);
}
*cl = malloc(length);
lseek(f,0,SEEK_SET);
if (read(f, *cl, length) != length)
{
perror("error");
return -2;
}
return 0;
}
//打印文件内容
void printdb(user * cl,int f)
{
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
int j;
char btime[TM_L + 1] = {0};
char name[10] = {0};
for (j = 0; j<length/i; j++)
{
memcpy(name,(cl+j) -> name, 10);
//memcpy(btime, (cl+j) -> btime,TM_L);
printf ("%s,%s,%s,%s,%ld,%d\n",
name,
(cl+j) ->password,
(cl+j) ->state,(cl+j)->IP,(cl+j)->port,(cl+j)->fd
);
}
}
//检测登陆是否成功
int check(user * cl,int f,char * name,char * password)
{
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
int j;
for (j=0; j<length/i; j++)
{
if (strcmp((cl+j)->name,name) == 0 && strcmp((cl+j)->password,password) == 0)
return j;
}
return -1;
}
//插入
int insertdb(user * cr,int f,user *cl)
{
int i;
i = sizeof(user);
int length = lseek(f,0,SEEK_END);
//cr->id = (cl + (length/i) - 1)->id + 1;
lseek(f,0,SEEK_END);
if (write(f,cr,i) != i)
{
perror("error");
return -1;
}
return 0;
}
//修改文件
int changedb(user * cc, int f, user *cl)
{
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
long j;
for (j=0; j<= length/i; j++)
{
if (strcmp((cl+j) -> name, cc -> name) == 0)
break;
}
printf ("nihao");
memcpy(cl+j,cc,i);
lseek(f,0,SEEK_SET);
printf ("%s\n",cl->name);
if (write(f,cl,length) != length)
{
perror("error");
return -1;
}
return 0;
}
//根据人名找到他的socket地址
struct sockaddr_in addr(char * name, int f, user *cl)
{
struct sockaddr_in toaddr;
bzero(&toaddr,sizeof(toaddr));
toaddr.sin_family = AF_INET;
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
long j;
for (j=0; j<= length/i; j++)
{
if (strcmp((cl+j) -> name, name) == 0)
break;
}
toaddr.sin_port = (cl+j) -> port;
printf ("%ld\n",(cl+j) -> port);
if (inet_aton("127.0.0.1",&toaddr.sin_addr) == 0)
{
printf ("addr convert error\n");
exit(1);
}
return toaddr;
}
//根据人名找到他的文件描述符
int fd(char * name, int f, user *cl)
{
int fd;
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
long j;
for (j=0; j<= length/i; j++)
{
if (strcmp((cl+j) -> name, name) == 0)
break;
}
printf ("(cl+j)->fd: %d\n",(cl+j)->fd);
fd = (cl+j)->fd;
return fd;
}
//根据socket找人名
char * name(struct sockaddr_in fromaddr, int f, user *cl)
{
char * name = (char *)calloc(10,sizeof(char));
int i = sizeof(user);
long length = lseek(f,0,SEEK_END);
long j;
for (j=0; j<= length/i; j++)
{
if ((cl+j) -> port == fromaddr.sin_port)
break;
}
printf ("%s\n",(cl+j)->name);
strcpy(name, (cl+j) -> name);
return name;
}
int main()
{
user cr[10];
user *cl;
user cc;
cl = NULL;
int sockfd, sin_size, new_fd,nbytes;
int i=0,loca[10],j=0,k,flag=0;
char buf[MAXDATASIZE];
struct sockaddr_in srvaddr,clientaddr;
char * p,*p1,*p2,*p3,*p4;
char command[10],cmd[10];
int client[10];
user user[100];
char bufp[200];
int len;
int f;
int res;
FILE * fp1,*fp2;
char file[50];
//创建套接字
sockfd = socket(AF_INET,SOCK_STREAM,0);
if (sockfd == -1)
{
printf ("create sockfd error!\n");
exit(1);
}
//为套接字分配地址
bzero(&srvaddr,sizeof(srvaddr));
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(PORT);
if (inet_aton("127.0.0.1",&srvaddr.sin_addr) == 0)
{
printf ("IP address transfer error!\n");
close(sockfd);
exit(1);
}
//绑定服务器
if (bind(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr)) == -1)
{
printf ("bind error!\n");
close(sockfd);
exit(1);
}
//监听端口
if (listen(sockfd,BACKLOG) == -1)
{
printf ("listen error\n");
close(sockfd);
exit(1);
}
f = open("qq.txt",O_RDWR | O_CREAT,0664);
if (f == -1)
{
perror("error");
return 1;
}
for(;;)
{
//接收客户端连接
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd,(struct sockaddr *)&clientaddr,&sin_size);
printf ("new_fd:%d\n",new_fd);
if (new_fd == -1)
{
printf ("accept error!\n");
continue;
}
printf ("new_fd:%d\n",new_fd);
if (!fork())
{
close(sockfd);
//接收客户端消息
nbytes = read(new_fd,buf,sizeof(buf));
buf[nbytes] = '\0';
printf ("receive message from client: %s\n",buf);
strcpy(bufp,buf);
p = strtok(buf," ");
strcpy(cmd,p);
printf ("%s\n",cmd);
printf ("%d\n",strcmp(cmd,"login"));
/*提取命令字——用户注册*/
if (strcmp(cmd,"register") == 0)
{
p1 = strtok(NULL," ");
strcpy(cr[i].name,p1);
p2 = strtok(NULL," ");
strcpy(cr[i].password,p2);
strcpy(cr[i].state,"out");
insertdb(&cr[i], f, cl);
res = selectdb(&cl,f);
if (res == -2)
{
return 1;
}
else if(res == 0)
{
printdb(cl,f);
}
bzero(buf,sizeof(buf));
strcpy(buf,"you have register successfully!");
write(new_fd,buf,sizeof(buf));
printf ("send messge to qqclient:%s\n",buf);
}
/*提取命令字——登陆及后续操作*/
if (strcmp(cmd,"login") == 0)
{
p1 = strtok(NULL," ");
p2 = strtok(NULL," ");
int m=0;
res = selectdb(&cl,f);
m = check(cl,f,p1,p2);
if (m >= 0)
{
strcpy(cc.name,p1);
strcpy(cc.password,p2);
strcpy(cc.state,"on");
strcpy(cc.IP,inet_ntoa(clientaddr.sin_addr));
cc.port = clientaddr.sin_port;
cc.fd = new_fd;
selectdb(&cl,f);
changedb(&cc,f,cl);
strcpy(buf,"you have login successfully!");
write(new_fd,buf,sizeof(buf));
printf ("send messge to qqclient:%s\n",buf);
bzero(buf,sizeof(buf));
res = selectdb(&cl,f);
if (res == -2)
{
return 1;
}
else if(res == 0)
{
printdb(cl,f);
}
}
while(1)
{
//接收客户端消息
nbytes = read(new_fd,buf,sizeof(buf));
buf[nbytes] = '\0';
printf ("receive message from client: %s\n",buf);
p = strtok(buf," ");
strcpy(cmd,p);
if (strcmp(cmd, "chat") == 0)
{
struct sockaddr_in toaddr;
char fromname[10];
char ss[10];
printf ("%s\n",buf);
p1 = strtok(NULL," ");
p2 = strtok(NULL," ");
strcpy(ss,p1);
res = selectdb(&cl,f);
printf ("%d\n",clientaddr.sin_port);
printf ("%s\n",name(clientaddr, f, cl));
strcpy(fromname,name(clientaddr, f, cl));
printf ("%s %s\n",p1,p2);
toaddr = addr(p1,f,cl);
int fdd;
printf("p1:%s\n",p1);
fdd = fd(p1, f, cl);
printf ("to port:%d\n",toaddr.sin_port);
sprintf(buf,"%s%s%s%s%s",cmd," ",fromname," ",p2);
printf ("%s\n",buf);
//write(new_fd,buf,sizeof(buf));
printf("p1:%s\n",p1);
printf ("judge:%d\n",fdd);
printf ("to port:%d %d\n",toaddr.sin_port,clientaddr.sin_port);
sendto(fdd,buf,strlen(buf),0,(struct sockaddr *)(&toaddr),sizeof(clientaddr));
printf ("send message:%s\n",buf);
}
}
}
close(new_fd);
exit(0);
}
close(new_fd);
n--;
}
close(sockfd);
return 0;
}




,楼主可以用消息队列将消息转过去啊,也可以直接把和请求回话的客户端的连接套接字发给下一个客户端,让他自己接收。
