利用Linux實現基於TCP模式的網絡聊天程序
主要完成的兩大組成部分為:服務器和客戶端。
以下簡單分析一下服務器端和客戶端兩個方面所要完成的任務。
服務器的主要功能如下:
在特定的端口上進行監聽,等待客戶端的連接。 用戶可以配置服務器端的監聽端口。 向連接的客戶端發送登錄成功信息。 向已經連接到服務器的客戶端的用戶發送系統消息。 使用TCP多線程並發服務器,向在線的所有客戶端發送消息客戶端的主要功能如下:
客戶端傳參輸入IP和端口號以及用戶名進行注冊 連接到已經開啟的服務的服務端 用戶可以向所有人發送信息 用戶可以接受服務器發送的系統消息服務器功能描述
服務器主要是負責監聽客戶端發送來的消息,利用TCP線程並發服務器模型實現對客戶端的監聽接收。
服務器程序的作用為:初始化服務器程序,持續監聽一個固定的端口,收到客戶的連接後建立一個socket連接,與客戶進行通信和信息處理,接收客戶通過socket連接發送來的數據,創建一個新的socket;通過socket連接與客戶通信,進行響應處理,並返回結果,通信結束後終端與客戶的連接(關閉socket);主要的過程為服務器創建一個共享內存空間函數,以及客戶端簡單的界面,利用套接字模型機制實現服務器簡易模型的實現,利用 socket()創建流式套接字,並可以返回套接自號;利用bind()實現套接字與本地地址相連,listen()通知TCP服務器准備好監聽客戶端的連接,accept()接收連接,等待客戶端的連接,建立連接之後accept返回新的標識客戶端的套接字,運用多線程以及recv()/send()接收發送數據;
客戶端功能描述
客戶端主要用來向服務器端發送數據,客戶端程序的作用:初始化客戶程序,連接到某個服務器上,建立socket連接,通過socket連接向服務器發送請求信息,通信結束後中斷與客戶的連接。主要的實現步驟為:需要定義運行時候需要的參數,同樣利用socket()建立流式套接字,返回套接字號,connect()將套接字與遠程主機連接,recv()和send()將套接字上的讀寫數據進行發送與接收,close()關閉套接字,關閉對話。
c.c
#include
#include
#include
#include #include
#include
#include
#include
#include
#include
#include
#define SIZE 1024
int main(int argc, char *argv[])
{
pid_t pid;
int sockfd,confd;
char buffer[SIZE],buf[SIZE];
struct sockaddr_in server_addr;
struct sockaddr_in client_addr;
struct hostent *host;
short port;
char *name;
//四個參數
if(argc!=4)
{
fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);
exit(1);
}
//使用hostname查詢host 名字
if((host=gethostbyname(argv[1]))==NULL)
{
fprintf(stderr,"Gethostname error\n");
exit(1);
}
port=atoi(argv[2]);
name=argv[3];
/*客戶程序開始建立 sockfd描述符 */
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
exit(1);
} else{
printf("Socket successful!\n");
}
/*客戶程序填充服務端的資料 */
bzero(&server_addr,sizeof(server_addr)); // 初始化,置0
server_addr.sin_family=AF_INET; // IPV4
server_addr.sin_port=htons(port); // (將本機器上的short數據轉化為網絡上的short數據)端口號
server_addr.sin_addr=*((struct in_addr *)host->h_addr); // IP地址
/* 客戶程序發起連接請求 */
if(confd=connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
exit(1);
}else{
printf("Connect successful!\n");
}
/*將客戶端的名字發送到服務器端*/
send(sockfd,name,20,0);
/*創建子進程,進行讀寫操作*/
pid = fork();//創建子進程
while(1)
{
/*父進程用於發送信息*/
if(pid > 0)
{
/*時間函數*/
struct tm *p;
time(&timep);
p = localtime(&timep);
strftime(buffer, sizeof(buffer), "%Y/%m/%d %H:%M:%S", p);
/*輸出時間和客戶端的名字*/
strcat(buffer," \n\t昵稱 ->");
strcat(buffer,name);
strcat(buffer,":\n\t\t ");
memset(buf,0,SIZE);
fgets(buf,SIZE,stdin);
/*對客戶端程序進行管理*/
if(strncmp("e",buf,1)==0)
{
printf("該客戶端下線...\n");
strcat(buffer,"退出聊天室!");
if((send(sockfd,buffer,SIZE,0)) <= 0)
{
perror("error send");
}
close(sockfd);
sockfd = -1;
exit(0);
}else
{
strncat(buffer,buf,strlen(buf)-1);
strcat(buffer,"\n");
if((send(sockfd,buffer,SIZE,0)) <= 0)
{
perror("send");
}
}
}
else if(pid == 0)
{
/*子進程用於接收信息*/
memset(buffer,0,SIZE);
if(sockfd > 0)
{
if((recv(sockfd,buffer,SIZE,0)) <= 0)
{
close(sockfd);
exit(1);
}
printf("%s\n",buffer);
}
}
} close(sockfd);
return 0;
}
s.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 4395
#define SIZE 1024
#define SIZE_SHMADD 2048
#define BACKLOG 3
int sockfd;
int fd[BACKLOG];
int i=0;
/*********套接字描述符*******/
int get_sockfd()
{ struct sockaddr_in server_addr; if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) { fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); exit(1); }else{
printf("Socket successful!\n"); } /*sockaddr結構 */
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
server_addr.sin_port=htons(PORT);
/*綁定服務器的ip和服務器端口號*/
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{ fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
exit(1);
} else{printf("Bind successful!\n"); }
/* 設置允許連接的最大客戶端數 */
if(listen(sockfd,BACKLOG)==-1)
{ fprintf(stderr,"Listen error:%s\n\a",strerror(errno)); exit(1); } else{
printf("Listening.....\n"); }
return sockfd;
}
/*創建共享存儲區*/
int shmid_create()
{ int shmid; if((shmid = shmget(IPC_PRIVATE,SIZE_SHMADD,0777)) < 0) { perror("shmid error!"); exit(1); }
Else printf("shmid success!\n");
return shmid;
}
int main(int argc, char *argv[]) { char shmadd_buffer[SIZE_SHMADD],buffer[SIZE]; struct sockaddr_in client_addr;
int sin_size;
pid_t ppid,pid; int new_fd;
int shmid;
char *shmadd;
/***********共享內存**************/
shmid = shmid_create();
//映射共享內存
shmadd = shmat(shmid, 0, 0);
/*****創建套接字描述符***********/
int sockfd = get_sockfd();
/*循環接收客戶端*/
while(1)
{ /* 服務器阻塞,直到客戶程序建立連接 */
sin_size=sizeof(struct sockaddr_in);
if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1)
{ fprintf(stderr,"Accept error:%s\n\a",strerror(errno)); exit1); }else{printf("Accept successful!\n"); }
fd[i++] = new_fd;
printf("\n已連接了客戶端%d : %s:%d \n",i , inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
/*把界面發送給客戶端*/
memset(buffer,0,SIZE);
strcpy(buffer,"\n——————————————————Welecom come char ———————————————————————\n");
send(new_fd,buffer,SIZE,0);
//創建子進程客戶端
ppid = fork(); if(ppid == 0)
{
//將加入的新客戶發送給所有在線的客戶端/
recv(new_fd,buffer,SIZE,0);
strcat( buffer," 進入了聊天室....");
for(i=0;i 0)
{
//父進程用於接收信息/
memset(buffer,0,SIZE);
if((recv(new_fd,buffer,SIZE,0)) <= 0)
{
close(new_fd);
exit(1); }
memset(shmadd, 0, SIZE_SHMADD);
strncpy(shmadd, buffer, SIZE_SHMADD);//將緩存區的客戶端信息放入共享內存裡
printf(" %s\n",buffer);
}
if(pid == 0)
{
//子進程用於發送信息/
sleep(1);//先執行父進程
if(strcmp(shmadd_buffer,shmadd) != 0)
{
strcpy(shmadd_buffer,shmadd);
if(new_fd > 0)
{
if(send(new_fd,shmadd,strlen(shmadd),0) == -1)
{
perror("send");
}
memset(shmadd, 0, SIZE_SHMADD);
strcpy(shmadd,shmadd_buffer);
}
}
}
}
}
} free(buffer);
close(new_fd);
close(sockfd);
return 0;
}
服務器進行編譯執行
客戶端進行編譯執行
服務器出現結果
另外打開一個終端執行對客戶端代碼編譯執行