RedHat下实现点对点聊天室-c语言

RedHat下实现点对点聊天室

点对点聊天室为人们进行交流提供了一个平台,且其具有极高的私密性与便捷性。通过提供完善的网络聊天系统,可以达到人与人之间的交流与联系的目的。

开发环境

VMware Workstation 15 Pro

Red Hat Enterprise Linux 5

每人一台计算机并能够连入局域网

系统实现的基本功能

(1)服务器:

 ① 接受客户端发送过来的数据并显示

 ② 接受键盘输入的数据,并发送给客户端

 ③ 客户端关闭,服务器端的进程也将全部退出。

(2)客户端:

 ① 接受服务器发送过来的数据并显示

 ② 接受键盘输入的数据,并发送给服务器

 ③ 服务器关闭,客户端的进程也将全部退出。

服务器端程序设计(p2pserv.c)



#include //是在程序编译之前要处理的内容,称为编译预处理命令。

#include //语句是指将 stdlib.h 包含到程序里面。

#include //是在程序编译之前要处理的内容,与字符串的调用有关。

#include //提供对 POSIX 操作系统 API 的访问功能的头文件的名称。

#include //主要定义了格式转换函数。

#include  //提供socket函数及数据结构。

#include //定义了程序执行时如何处理不同的信号。

/*错误重定义*/

#define ERR_EXIT(m)\

do\

{\

        perror(m);\//将上一个函数发生错误的原因输出到标准设备。

        exit(EXIT_FAILURE);\//
代表异常退出。

}while(0)

/*信号处理函数*/ 

void handler(int sig)

{

    printf("recv
a sig=%d\n", sig);

    exit(EXIT_SUCCESS);//
指示程序成功终止

}

/*开始主程序*/

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

{

int listenfd; //定义监听套接字、客户套接字

int connfd; 

struct sockaddr_in servaddr; 

struct sockaddr_in clntaddr; 

socklen_t clntaddr_size; 

pid_t pid; //定义线程标记变量

if(argc!=2) 

{ 

  
printf("usage:%s,\n",argv[0]); 

  
exit(1); 

}

/*创建服务器端套接字*/

listenfd=socket(PF_INET,SOCK_STREAM,0); //生成一个TCP的socket

if(listenfd==-1) 

   ERR_EXIT("socket");


/*初始化本地地址信息*/

memset(&servaddr,0,sizeof(servaddr)); //作用是在一段内存块中填充某个给定的值

servaddr.sin_family=AF_INET; //创建套接字时,用该字段指定地址家族

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
//监听客户端的所有地址

servaddr.sin_port=htons(atoi(argv[1])); 

/*将地址信息分配给套接字*/

if(bind(listenfd,(struct sockaddr
*)&servaddr,sizeof(servaddr))==-1)

    ERR_EXIT("bind");


/*调用listen函数进入等待连接请求状态,此时套接字成为被动套接字,负责监听*/

if(listen(listenfd,5)==-1) 

   
ERR_EXIT("listen");

/*调用accept函数从队头取1个连接请求与客户端建立连接,并返回创建的套接字描述符,conn_sock主要用来与客户进行通信,而之前的serv_sock主要用于监听*/

connfd=accept(listenfd,(struct sockaddr
*)&clntaddr,&clntaddr_size); 

if(connfd==-1) 

   
ERR_EXIT("accept"); 

else 

     printf("recv connect
ip=%s,port=%d\n",inet_ntoa(clntaddr.sin_addr),ntohs(clntaddr.sin_port)); //输出

pid_t pid;

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

if(pid==-1)

   
ERR_EXIT("fork");

if(pid>0) //父进程,负责接收客户端发送过来的数据 

{

   
char recvbuf[1024];

while(1)

   
{ 

      
memset(recvbuf,0,sizeof(recvbuf));

      
int str_len=read(connfd,recvbuf,sizeof(recvbuf));

      
if(str_len==-1)

          
ERR_EXIT("read");

      
if(str_len==0)

      
{

      
    printf("Client closed\n");

          
break;

      
}

printf("Message from client:");

      
fputs(recvbuf,stdout);

    
}

      
close(connfd);

      
close(listenfd);

      
printf("parent close\n");

          kill(pid, SIGUSR1); //调用kill函数将信号发送给子进程 

        exit(EXIT_SUCCESS);

}

else if(pid==0)

{

  
signal(SIGUSR1, handler);//一旦接收到SIGUSR1信号就调用handler函数进行处理 

    char
sendbuf[1024];

        
       while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)

  {

       
write(connfd,sendbuf,sizeof(sendbuf));

       
memset(sendbuf,0,sizeof(sendbuf));

  } 

}

 
return 0;

}

客户端程序设计(p2pclint.c)



#include //是在程序编译之前要处理的内容,称为编译预处理命令。

#include //语句是指将 stdlib.h 包含到程序里面。

#include //是在程序编译之前要处理的内容,与字符串的调用有关。

#include //提供对 POSIX 操作系统 API 的访问功能的头文件的名称。

#include //主要定义了格式转换函数。

#include  //提供socket函数及数据结构。

#include//定义了程序执行时如何处理不同的信号。

/*错误宏定义*/

#define ERR_EXIT(m)\ //定义错误出口

do\ 

{\ 

  
perror(m);\ 

  
exit(EXIT_FAILURE);\ //代表异常退出

}while(0) 

/*信号处理函数*/ 

void handler(int sig)

{

    printf("recv
a sig=%d\n", sig);

    exit(EXIT_SUCCESS);//指示程序成功终止

}

/*开始主程序*/

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

{ 

   int
sock; 

  
struct sockaddr_in servaddr;// 声明了一个struct
sockaddr_in类型的变量

    char
sendbuf[1024];// 定义了一个大小为1024的sendbuf数组

    if(argc!=3) //获取信息错误的提示。但是不停止运行任何代码

   {


printf("usage:%s,<IP>
<port>\n",argv[0]); 

     
exit(1); 

} 

/*创建准备连接服务器端的套接字*/ 

 
sock=socket(PF_INET,SOCK_STREAM,0); // socket()系统调用,带有三个参数

 
if(sock==-1) 

     
ERR_EXIT("socket"); 

memset(&servaddr,0,sizeof(servaddr)); 

  servaddr.sin_family=AF_INET;
//创建套接字时,用该字段指定地址家族,对于TCP/IP协议的,必须设置为AF_INTE。

servaddr.sin_addr.s_addr=inet_addr(argv[1]);


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

  /*与服务器建立连接*/ 

if(connect(sock,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)


     
ERR_EXIT("connect"); 

 
else 

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

 
pid_t pid;// 定义了一个pid_t类型的变量pid

 
pid=fork();//fork()函数返回一个进程号,这个进程号赋给了pid

  /*父进程,负责从键盘接收数据,并传输给服务器端*/

 
if(pid>0)

  {

    
signal(SIGUSR1, handler);//一旦接收到SIGUSR1信号就调用handler函数进行处理 

      while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL)

    
{

    
    write(sock,sendbuf,sizeof(sendbuf));

         memset(sendbuf,0,sizeof(sendbuf));

       
}

   
}

/*子进程,负责从服务器接收数据并显示*/ 

else if(pid==0)

 { 

     char recvbuf[1024];// 定义了一个大小为1024的recvbuf数组

     
while(1)

     
{

          
int str_len=read(sock,recvbuf,sizeof(recvbuf));

       
   if(str_len==0)

         
{ 

     
           printf("server closed\n");

                 break;

          
} 

else if(str_len<0)

                ERR_EXIT("read");

           printf("Message from
server:");

           fputs(recvbuf,stdout);

           memset(recvbuf,0,sizeof(recvbuf));

        } 

      close(sock);

       kill(getppid( ), SIGUSR1);////调用kill函数将信号发送给

   }

 
else

     
ERR_EXIT("fork");

 
return 0;

}

运行结果

  1. 将编辑好的客户端“p2pclint.c”文件和服务器“p2pserv.c”文件放在创建好的“ptp_chat_room”文件夹下,如下图所示。
    RedHat下实现点对点聊天室-c语言_第1张图片

  2. 在此文件下点击鼠标右键弹出菜单,选择“在终端中打开”打开终端。
    RedHat下实现点对点聊天室-c语言_第2张图片

  3. 在终端窗口中,输入gcc命令对客户端与服务器文件进行编译。
    RedHat下实现点对点聊天室-c语言_第3张图片

  4. 重新打开一个终端窗口,输入“./p2pserv 5678”打开服务器(5678是端口号)。
    RedHat下实现点对点聊天室-c语言_第4张图片

  5. 重新打开一个终端窗口,输入“./p2pclint 127.0.0.1 5678”打开客户端
    RedHat下实现点对点聊天室-c语言_第5张图片

  6. 在两个窗口中发送消息均可接收到,运行成功。
    RedHat下实现点对点聊天室-c语言_第6张图片

你可能感兴趣的:(c语言)