TCP實現簡單聊天程式
阿新 • • 發佈:2018-11-01
上次我們通過UDP來實現了簡單的聊天程式,這次我們用TCP協議來實現,TCP和UDP不同的是TCP需要建立連線。
//這是一個通過TCP協議來實現聊天的程式 //1.建立socket //2.為socket繫結地址 //3.向服務端傳送連結請求 //4.傳送資料 //5.接受資料 //6.斷開連線 // #include<stdio.h> #include<stdlib.h> #include<string.h> #include<errno.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<unistd.h> int main(int argc,char *argv[]) { if(argc!=3) { printf("please input:tcp ip port\n"); return -1; } int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sockfd<0) { perror("socket error\n"); return -1; } //2.和UDP協議一樣客戶端不推薦繫結埠地址 //3.向伺服器端傳送連結請求 struct sockaddr_in cli_addr; cli_addr.sin_family=AF_INET; cli_addr.sin_port=htons(atoi(argv[2])); cli_addr.sin_addr.s_addr=inet_addr(argv[1]); //通過命令列引數來給結構體賦值 socklen_t len=sizeof(struct sockaddr_in); int ret=connect(sockfd,(struct sockaddr*)&cli_addr,len); //connect 向服務端傳送連結請求 //第一個引數是sockfd也就是操作描述符 //第二個引數為要連線的伺服器地址 //第三個引數為地址資訊的長度 //返回值如果成功為0,失敗為-1 if(ret<0) { perror("connect error\n"); return -1; } while(1) { //傳送資料 char buff[1024]={0}; scanf("%s",buff); send(sockfd,buff,strlen(buff),0); //send 函式用來給已經連結的socket傳送資料 //第一個引數為sockfd 操作符 //第二個引數為傳送的資料 //第三個引數為傳送的資料長度 //第四個引數為執行方式0位阻塞 // //接受資料 memset(buff,0x00,1024); ssize_t rlen=recv(sockfd,buff,1023,0); //recv 函式用來接受已經連結的socket接受資料 //第一個引數是sockfd操作符 //第二個引數為接受資料的緩衝區 //第三個引數為接受資料的長度 //第四個引數0為阻塞接受 if(rlen<=0) { perror("recv error\n"); return -1; } printf("server say:%s",buff); } close(sockfd); return 0; }
這是聊天程式的客戶端,對於客戶端和UDP很相似不過需要向服務端傳送connect連線,只有連線上了之後兩個程式才能傳送資料。
//這是TCP協議來實現服務端的程式 //1.建立socket //2.為socket繫結地址 //3.開始監聽,可以接受客戶端的連結請求 //4.獲取連線成功的socket //5.接受資料 //6.傳送資料 //7.關閉socket // // #include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<errno.h> #include<string.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> int main(int argc,char *argv[]) { if(argc!=3) { printf("please input:tcp ip port\n"); return -1; } int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if(sockfd<0) { perror("socker error\n"); return -1; } struct sockaddr_in ser_addr; ser_addr.sin_family=AF_INET; ser_addr.sin_port=htons(atoi(argv[2])); ser_addr.sin_addr.s_addr=inet_addr(argv[1]); socklen_t len=sizeof(struct sockaddr_in); int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len); //為socket繫結地址 if(ret<0) { perror("bind error\n"); return -1; } //服務端開始監聽 這時候服務端可以請求連結 if(listen(sockfd,5)<0) { //listen 函式服務端用來監聽 //第一個引數為socket描述符 //第二個引數為 最大的同時併發連線數 perror("listen error\n"); return -1; } while(1) { int new_sockfd; struct sockaddr_in addr; len=sizeof(struct sockaddr_in); new_sockfd=accept(sockfd,(struct sockaddr*)&addr,&len); //獲取連線成功的socket int accept //第一個引數為socket描述符 //第二個引數為新建立連結的客戶端地址資訊 //第三個引數為地址資訊的長度 //返回值如果成功返回新的描述符,如果失敗返回-1 //accept是一個阻塞函式連結成功佇列中如果沒有新的連結 //那就會一直阻塞直到有新的客戶端連結到來 //這裡我們通過創新新的socket來接受資料 //客戶端傳送的資料都在這個新的socket緩衝區中 //以前的socket被用來處理連結,連結成功的socket //傳送資料都在新的socket的緩衝區 if(new_sockfd<0) { perror("accept error\n"); continue;//這裡不能直接退出,因為這個連結失敗了可以連結別的socket } printf("new socket:%s,%d,\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port)); //這裡我們輸出一下連結過來的socket的ip地址和port埠號 while(1) { //接受資料 char buff[1024]={0}; ssize_t rlen=recv(new_sockfd,buff,1023,0); //recv 接受資料 //第一個為sockfd描述符 //第二個引數為接受資料的緩衝區 //第三個為接受資料的長度 //第四個引數為0是代表阻塞式接受 //對於返回值有三種情況 如果錯誤返回-1,連結關閉返回0,正確的話返回的是接受的長度 if(rlen<0) { perror("recv error\n"); close(new_sockfd); continue;//這裡和客戶端不同不能直接關閉你的程式 } else if(rlen==0) { printf("perr shutdown\n"); close(new_sockfd); continue;//如果返回0的話代表那個連結斷開了 } printf("client %s:%d say:%s\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port),buff); //傳送資料 memset(buff,0x00,1024); scanf("%s",buff); send(new_sockfd,buff,strlen(buff),0); } } close(sockfd); return 0; }