1.实验项目名称:基于Linux的TCP网络聊天室
2.实验目的:通过TCP完成多用户群聊和私聊功能。
3.实验过程:
通过socket建立用户连接并传送用户输入的信息,分别来写客户端和服务器端,利用多线程来实现多用户模式,服务器端随时准备接收客户端发送的消息,并判断该消息类型(私聊或群聊)来进行对应的转发工作,客户端随时接受来自服务器端的消息,从而实现消息的同步。
(1)开启服务器。
(2)开启客户端,输入用户昵称,客户端开始与服务器建立连接。
(3)群聊功能,一名用户发送消息,聊天室的其他成员均可收到此消息。
(4)私聊功能,一位用户@其他用户,则此消息只有被@的用户可以收到。
用户的信息借助结构体来存放,群聊功能通过遍历结构体,把消息发送给每一名用户,私聊功能通过对用户所发消息进行判断,若消息中存在@其他用户则把该消息私发给指定用户。
另外,私聊功能函数可以进行对各种@操作的判断,如用户所@的人不存在,则把此消息作为群消息发出,@其他用户后没有添加空格,则把此消息视为群发消息。
源码:
头文件
1 //header.h
2 #ifndef _A_H
3 #define _A_H
4 #include <stdio.h>
5 #include <pthread.h>
6 #include <sys/socket.h>
7 #include <netinet/in.h>
8 #include <arpa/inet.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <signal.h>
13 #include <pthread.h>
14 struct client{
15 char name[30]; //客户端名字
16 int fds; //客户端socket描述符
17 };
18 struct client c[100]={0};//接收客户端100个
19 int in; //数组下标
20 char *IP ="127.0.0.1"; //ip
21 short PORT=10222;
22 char NAME[20]={}; //@的名字
23 int FLAG=0; //标记是群发还是私聊
24 #endif
服务器端
1 //tcp_s.c
2 #include "header.h"
3 int sockfd; //服务器socket
4 char tempName[20]={};
5 //初始化服务器网络
6 void init(){
7 printf("start chat room...");
8 sockfd = socket(PF_INET,SOCK_STREAM,0);
9 if(sockfd == -1){
10 perror("create socket false\n");
11 exit(-1);
12 }
13 struct sockaddr_in addr; //网络通信地址结构
14 addr.sin_family = PF_INET; //协议簇
15 addr.sin_port = htons(PORT); //端口
16 addr.sin_addr.s_addr = inet_addr(IP); //IP地址
17 if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) == -1){
18 perror("bind false\n");
19 exit(-1);
20 }
21 if(listen(sockfd,100) == -1){
22 perror("Setup listening failed\n");
23 exit(-1);
24 }
25 printf("Server initialization succeeded!\n");
26 }
分发消息
1 void SendMessage(char *msg){
2 int flag=0; //标记@符号是否为私聊
3 int exit=0; //比较@的用户是否存在
4 int i=0,j=0;
5 while(msg[i]!=':'){
6 if(i>=strlen(msg))
7 break;
8 i++;
9 } //找到@符号位置
10 if(msg[i+1] == '@'){
11 i++;
12 while(msg[i]!=32){
13 if(i>=strlen(msg)){ //@符号不是私聊作用
14 flag=1;
15 break;
16 }
17 NAME[j]=msg[i+1];
18 i++;
19 j++;
20 }
21 if(flag){ //群发
22 for(i=0;i<in;i++){
23 printf("send to %d\n",c[i].fds);
24 send(c[i].fds,msg,strlen(msg),0);
25 }
26 }
27 else{ //私发
28 for(i=0;i<in;i++){
29 int k=0;
30 for(k=0;k<strlen(NAME)-1;k++){
31 tempName[k]=NAME[k];
32 }
33 if(strcmp(c[i].name,tempName)==0){
34 send(c[i].fds,msg,strlen(msg),0);
35 exit=1;
36 break;
37 }
38 }
39 }
40 }
41 else{
42 exit = 1;
43 for(i=0;i<in;i++){
44 printf("send to %d\n",c[i].fds);
45 send(c[i].fds,msg,strlen(msg),0);
46 }
47 }
48 if(exit == 0){
49 for(i=0;i<in;i++){
50 printf("send to %d\n",c[i].fds);
51 send(c[i].fds,msg,strlen(msg),0);
52 }
53 }
54 }
线程函数中进行通信/接收客户端消息,分发给所有客户端
1 void *server_thread(void *p){
2 char name[30]={};
3 if(recv(c[in].fds,name,sizeof(name),0)>0){
4 name[strlen(name)]='\0';
5 strcpy(c[in].name,name);
6 }
7 in++;
8 char tip[100]={};
9 sprintf(tip,"%s join in the chat room\n",c[in-1].name);
10 SendMessage(tip);
11 int fd = *(int*)p;
12 printf("pthread = %d\n",fd);
13 while(1){
14 char buf[100]={};
15 if(recv(fd,buf,sizeof(buf),0) == 0){
16 //表示退出连接
17 printf("fd = %d left the chat room\n",fd);
18 int i,j;
19 char name[20]={};
20 int flag = 1;
21 for(i=0;i<in;i++){
22 if(c[i].fds == fd){
23 strcpy(name,c[i].name);
24 i++;
25 flag = 0;
26 }
27 if(flag != 1){
28 c[i-1].fds = c[i].fds;
29 strcpy(c[i-1].name,c[i].name);
30 }
31 }
32 c[i].fds = 0;
33 strcpy(c[i].name,"");
34 in--;
35 char msg[100];
36 sprintf(msg,"%s left the chat room\n",name);
37 SendMessage(msg);
38 close(fd);
39 break;
40 }
41 SendMessage(buf);
42 }
43 }
等待客户端连接,启动服务器的服务
1 void server(){
2 printf("Server starts service!\n");
3 while(1){
4 struct sockaddr_in fromaddr; //存储客户端通信地址
5 socklen_t len = sizeof(fromaddr);
6 int fd = accept(sockfd,(struct sockaddr*)&fromaddr,&len);
7 if(fd == -1){
8 perror("Client connection failed!\n");
9 continue;
10 }
11 c[in].fds = fd;
12 pthread_t pid;
13 pthread_create(&pid,0,server_thread,&fd);
14 }
15 }
16 void sig_close(){
17 close(sockfd);
18 printf("Server close\n");
19 exit(0);
20 }
21 int main()
22 {
23 signal(SIGINT,sig_close);
24 init();
25 server();
26 return 0;
27 }
客户端
1 //tcp_c.c
2 #include "header.h"
3 char name[30]; //name
4 int sockfd; //客户端socket
5 int mutax=0; //互斥变量
6 void init(){
7 printf("Client starts\n");
8 sockfd =socket(PF_INET,SOCK_STREAM,0);
9 struct sockaddr_in addr;
10 addr.sin_family =PF_INET;
11 addr.sin_port=htons(PORT);
12 addr.sin_addr.s_addr=inet_addr(IP);
13 if(connect(sockfd,(struct sockaddr*)&addr,sizeof (addr))==-1){
14 perror("connect failed\n");
15 exit(-1);
16 }
17 }
通信
1 void start(){
2 pthread_t pid;
3 void* receive_thread(void*);
4 pthread_create(&pid,0,receive_thread,0);
5 while(1){
6 char buf[100]={};
7 gets(buf);
8 char msg[100]={};
9 if(mutax){
10 mutax=0;
11 continue;
12 }
13 sprintf(msg,"%s said:%s",name,buf);
14 send(sockfd,msg,strlen(msg),0);
15 }
16
17 }
18 void* receive_thread(void *p){
19 while(1){
20 char buf[100]={};
21 if(recv(sockfd,buf,sizeof(buf),0)<=0){
22 break;
23 }
24 printf("%s\n",buf);
25 }
26 }
27 void sig_close(){
28 close(sockfd);
29 exit(0);
30 }
31 int main(){
32 signal(SIGINT,sig_close);
33 printf("Please input your name:");
34 scanf("%s",name);
35 mutax=1;
36 init();
37 if(mutax)
38 send(sockfd,name,strlen(name),0);
39 start();
40 return 0;
41 }