SOCKET网络编程:Linux下实现聊天室

  程序介绍:本聊天室程序在Ubuntu下,采用C语言实现,结构为Client/Server结构;

           服务端程序通过共享存储区存储聊天数据,并发送给每个连接的客户端;

 服务端程序和客户端程序都是通过父子进程分别负责发送和接收数据的,以避免数据冲撞;

 需按以下格式调用客户端程序:client.exe 服务端主机IP 端口号(本程序设定为:3490) 用户名(在聊天室中显示的用户名)

 

 程序截图:

//--------------------------------服务端----------------------------------------------

 

//--------------------------------客户端1:真水无香--------------------------------------

 

//--------------------------------客户端2:蜡笔小新--------------------------------------

 

程序代码如下:

//--------------------------------server.c--------------------------------------------------

//包含工程所需的头文件

#include<stdio.h>  

#include<stdlib.h>

#include<sys/types.h>  //数据类型定义

#include<sys/stat.h>

#include<netinet/in.h>  //定义数据结构sockaddr_in

#include<sys/socket.h>  //提供socket函数及数据结构

#include<string.h>

#include<unistd.h>

#include<signal.h>

#include<sys/ipc.h>

#include<errno.h>

#include<sys/shm.h>

#include<time.h>

#define PERM S_IRUSR|S_IWUSR 

#define MYPORT 3490  //宏定义定义通信端口

#define BACKLOG 10 //宏定义,定义服务程序可以连接的最大客户数量

#define WELCOME "|----------Welcome to the chat room! ----------|"  //宏定义,当客户端连接服务端时,想客户发送此欢迎字符串

//转换函数,将int类型转换成char *类型

void itoa(int i,char*string)

{

   int power,j;

   j=i;

   for(power=1;j>=10;j/=10)

     power*=10;

   for(;power>0;power/=10)

   {

     *string++='0'+i/power;

     i%=power;

   }

   *string='\0';

}

 

//得到当前系统时间

void get_cur_time(char * time_str)

{

   time_t timep;

   struct tm *p_curtime;

   char *time_tmp;

   time_tmp=(char *)malloc(2);

   memset(time_tmp,0,2);

 

   memset(time_str,0,20);

   time(&timep);

   p_curtime = localtime(&timep);

   strcat(time_str," (");

   itoa(p_curtime->tm_hour,time_tmp);

   strcat(time_str,time_tmp);

   strcat(time_str,":");

   itoa(p_curtime->tm_min,time_tmp);

   strcat(time_str,time_tmp);

   strcat(time_str,":");

   itoa(p_curtime->tm_sec,time_tmp);

   strcat(time_str,time_tmp);

   strcat(time_str,")");

   free(time_tmp);

}

//创建共享存储区

key_t shm_create()

{

   key_t shmid;

   //shmid = shmget(IPC_PRIVATE,1024,PERM);

   if((shmid = shmget(IPC_PRIVATE,1024,PERM)) == -1)

   {

     fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno));

     exit(1);

   }

   return shmid;

}

//端口绑定函数,创建套接字,并绑定到指定端口

int bindPort(unsigned short int port)

{ 

   int sockfd;

   struct sockaddr_in my_addr;

   sockfd = socket(AF_INET,SOCK_STREAM,0);//创建基于流套接字

   my_addr.sin_family = AF_INET;//IPv4协议族

   my_addr.sin_port = htons(port);//端口转换

   my_addr.sin_addr.s_addr = INADDR_ANY;

   bzero(&(my_addr.sin_zero),0);

 

   if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)) == -1)

   {

     perror("bind");

     exit(1);

   }

   printf("bing success!\n");

   return sockfd;

}

int main(int argc, char *argv[])

{

   int sockfd,clientfd,sin_size,recvbytes; //定义监听套接字、客户套接字

   pid_t pid,ppid;  //定义父子线程标记变量

   char *buf, *r_addr, *w_addr, *temp, *time_str;//="\0"; //定义临时存储区

   struct sockaddr_in their_addr;  //定义地址结构

   key_t shmid;

 

   shmid = shm_create(); //创建共享存储区

 

   temp = (char *)malloc(255);

   time_str=(char *)malloc(20);

   sockfd = bindPort(MYPORT);//绑定端口

   while(1)

   {    

     if(listen(sockfd,BACKLOG) == -1)//在指定端口上监听

     {

       perror("listen");

       exit(1);

     }

     printf("listening......\n");

     if((clientfd = accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)) == -1)//接收客户端连接

     {

       perror("accept");

       exit(1);

     }

     printf("accept from:%d\n",inet_ntoa(their_addr.sin_addr));

     send(clientfd,WELCOME,strlen(WELCOME),0);//发送问候信息

     buf = (char *)malloc(255);

   

     ppid = fork();//创建子进程

     if(ppid == 0)

     {

       //printf("ppid=0\n");

       pid = fork();  //创建子进程  

       while(1)

       {

         if(pid > 0)

         {

             //父进程用于接收信息

           memset(buf,0,255);

           //printf("recv\n");

           //sleep(1);

           if((recvbytes = recv(clientfd,buf,255,0)) <= 0)

           {

             perror("recv1");

             close(clientfd);

             raise(SIGKILL);

             exit(1);

           }

           //write buf's data to share memory

           w_addr = shmat(shmid, 0, 0);

                memset(w_addr, '\0', 1024);

           strncpy(w_addr, buf, 1024);

           get_cur_time(time_str);

           strcat(buf,time_str);

           printf(" %s\n",buf);

         }

         else if(pid == 0)

         {

             //子进程用于发送信息

           //scanf("%s",buf);

           sleep(1);

           r_addr = shmat(shmid, 0, 0);

           //printf("---%s\n",r_addr);

           //printf("cmp:%d\n",strcmp(temp,r_addr));

           if(strcmp(temp,r_addr) != 0)

           {

             strcpy(temp,r_addr);

             get_cur_time(time_str);           

             strcat(r_addr,time_str);

            //printf("discriptor:%d\n",clientfd);

             //if(send(clientfd,buf,strlen(buf),0) == -1)

             if(send(clientfd,r_addr,strlen(r_addr),0) == -1)

             {

               perror("send");

             }

             memset(r_addr, '\0', 1024);

             strcpy(r_addr,temp);

           }

         }

         else

           perror("fork");

       }

     }

   }

   printf("------------------------------\n");

   free(buf);

   close(sockfd);

   close(clientfd);

   return 0;

}

 

//-----------------------------client.c-------------------------------------------------

//包含工程所需的头文件

#include<stdio.h>

#include<netinet/in.h>  //定义数据结构sockaddr_in

#include<sys/socket.h>  //提供socket函数及数据结构

#include<sys/types.h> //数据类型定义

#include<string.h>

#include<stdlib.h>

#include<netdb.h>

#include<unistd.h>

#include<signal.h>

#include<time.h>

int main(int argc, char *argv[])

{

   struct sockaddr_in clientaddr;//定义地址结构

   pid_t pid;

   int clientfd,sendbytes,recvbytes;//定义客户端套接字

   struct hostent *host;

   char *buf,*buf_r;

   if(argc < 4)

   {

    printf("usage:\n");

    printf("%s host port name\n",argv[0]);

    exit(1);

   }

   host = gethostbyname(argv[1]);

   if((clientfd = socket(AF_INET,SOCK_STREAM,0)) == -1)  //创建客户端套接字

   {

     perror("socket\n");

     exit(1);

   }

   //绑定客户端套接字

   clientaddr.sin_family = AF_INET;

   clientaddr.sin_port = htons((uint16_t)atoi(argv[2]));

   clientaddr.sin_addr = *((struct in_addr *)host->h_addr);

   bzero(&(clientaddr.sin_zero),0);

   if(connect(clientfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr)) == -1) //连接服务端

   {

     perror("connect\n");

     exit(1);

   }

   buf=(char *)malloc(120);

   memset(buf,0,120);

   buf_r=(char *)malloc(100);

 

   if( recv(clientfd,buf,100,0) == -1)

   {

     perror("recv:");

     exit(1);

   }

   printf("\n%s\n",buf);

 

   pid = fork();//创建子进程

   while(1)

   {

     if(pid > 0){

         //父进程用于发送信息

 

       //get_cur_time(time_str);

 

       strcpy(buf,argv[3]);

       strcat(buf,":");

       memset(buf_r,0,100);

       //gets(buf_r);

       fgets(buf_r,100,stdin);

       strncat(buf,buf_r,strlen(buf_r)-1);

       //strcat(buf,time_str);

       //printf("---%s\n",buf);

       if((sendbytes = send(clientfd,buf,strlen(buf),0)) == -1)

       {

         perror("send\n");

         exit(1);

       }

     }

     else if(pid == 0)

     {

      //子进程用于接收信息

       memset(buf,0,100);

       if(recv(clientfd,buf,100,0) <= 0)

       {

         perror("recv:");

         close(clientfd);

         raise(SIGSTOP);

         exit(1);

       }

       printf("%s\n",buf);

     }

     else

       perror("fork");

   }

   close(clientfd);

   return 0;    

}

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值