日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

基于Linux的Socket编程之TCP全双工Server-Client聊天程序

發布時間:2023/11/30 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于Linux的Socket编程之TCP全双工Server-Client聊天程序 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載:http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a40613cfe1

一、引言:

由于accept函數、read、write、recv、send等函數都是是阻塞式的,在同一個進程之中,只要有任何一個函數沒有執行完畢,處于阻塞狀態,之后的函數與功能就不能處理,很難實現點對點的Server-Client全雙工通信。因為全雙工通信是非阻塞式的通信方式,即使對方沒有回復消息,都可以隨時發送。如果只是電報機式的半雙工通信,之前已經基本實現:基于Linux的SOCKET編程之TCP半雙工Client-Server聊天程序?
而對于QQ點對點聊天式的全雙工通信,又該怎樣實現呢?對于當前所學只能想到使用fork函數創建一個子進程,其中父進程用來處理發(或者收),而子進程用來處理收(或者發)的過程。fork函數的一些基本的使用可參照:進程創建與fork()的恩怨情仇

二、測試代碼:

測試環境(Redhat 6.4)

1、客戶端(Client):

# include<stdio.h> # include<stdlib.h> # include<string.h> # include<unistd.h> # include<sys/socket.h> # include<arpa/inet.h> # include<netinet/in.h> # include<signal.h># define MAX_BUF_LEN 128/*處理系統調用中產生的錯誤*/ void error_print(char * ptr) {perror(ptr);exit(EXIT_FAILURE); } /*處理通信結束時回調函數接收到的信號*/ void quit_tranmission(int sig) {printf("recv a quit signal = %d\n",sig);exit(EXIT_SUCCESS); } int main(void) {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0)error_print("socket");struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = 1234;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");/*inet_aton("127.0.0.1",&servaddr.sin_addr);*/int conn;if((conn = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))) < 0)error_print("connect");pid_t pid;pid = fork();if(pid == -1){error_print("fork");}if(pid == 0){char recv_buf[MAX_BUF_LEN] = {0};while(1){bzero(recv_buf,sizeof(recv_buf));int ret = read(sockfd, recv_buf, sizeof(recv_buf));if(ret == -1)error_print("read");else if(ret == 0){printf("server is close!\n");break;//子進程收到服務器端退出的信息(服務器Ctrl+C結束通信進程,read函數返回值為0,退出循環)}fputs(recv_buf,stdout);/*將收到的信息輸出到標準輸出stdout上*/}close(sockfd);/*子進程退出,通信結束關閉套接字*/kill(getppid(),SIGUSR1);/*子進程結束,也要向父進程發出一個信號告訴父進程終止接收,否則父進程一直會等待輸入*/exit(EXIT_SUCCESS);/*子進程正常退出結束,向父進程返回EXIT_SUCCESS*/}else{signal(SIGUSR1,quit_tranmission);/*回調函數處理通信中斷*/char send_buf[MAX_BUF_LEN] = {0};/*如果服務器Ctrl+C結束通信進程,fgets獲取的就是NULL,否則就進入循環正常發送數據*/while(fgets(send_buf,sizeof(send_buf), stdin) != NULL){int set = write(sockfd, send_buf, strlen(send_buf));/*將send_buf緩沖區的數據發送給對端服務器*/if(set < 0)error_print("write");bzero(send_buf,strlen(send_buf));}close(sockfd);/*通信結束,關閉套接字*/}return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

2、服務器(Server):

# include<stdio.h> # include<stdlib.h> # include<string.h> # include<unistd.h> # include<sys/socket.h> # include<arpa/inet.h> # include<netinet/in.h> # include<signal.h># define MAX_BUF_LEN 128void error_print(char * ptr) {perror(ptr);exit(EXIT_FAILURE); }void quit_tranmission(int sig) {printf("recv a quit signal = %d\n",sig);exit(EXIT_SUCCESS); } int main(void) {int sockfd = socket(AF_INET, SOCK_STREAM, 0);/*IPV4流式協議即TCP協議*/if(sockfd < 0)error_print("socket");struct sockaddr_in servaddr;bzero(&servaddr,sizeof(servaddr));servaddr.sin_family = AF_INET;/*IPV4*/servaddr.sin_port = 1234;servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");/*使用本地環回地址做測試*//*inet_aton("127.0.0.1",&servaddr.sin_addr);//與inet_addr函數作用相同*//*setsockopt確保服務器不用等待TIME_WAIT狀態結束就可以重啟服務器,繼續使用原來的端口號*/int on = 1;if( setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)error_print("setsockopt");/*綁定本地Socket地址*/if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)error_print("bind");/*監聽連接*/if(listen(sockfd, SOMAXCONN) < 0)error_print("listen");struct sockaddr_in peeraddr;/*存儲連接成功的客戶端Socket信息*/socklen_t peerlen = sizeof(peeraddr);int conn;/*接收監聽隊列第一個完成連接的請求*/if((conn = accept(sockfd,(struct sockaddr*)&peeraddr,&peerlen)) < 0)error_print("accept");pid_t pid;pid = fork();/*創建一個新的子進程*/if(pid == -1){error_print("fork");}if(pid == 0){/*子進程中用來向客戶端發送數據*/signal(SIGUSR1,quit_tranmission);/*回調函數處理通信中斷*/char send_buf[MAX_BUF_LEN]={0};/*如果客戶端Ctrl+C結束通信進程,fgets獲取的就是NULL,否則就進入循環正常發送數據*/while(fgets(send_buf, sizeof(send_buf), stdin) != NULL){write(conn,send_buf,strlen(send_buf));bzero(send_buf,strlen(send_buf));/*發送完成清空發送緩沖區*/}exit(EXIT_SUCCESS);/*成功退出子進程*/}else{char recv_buf[MAX_BUF_LEN]={0};while(1){bzero(recv_buf,strlen(recv_buf));int ret = read(conn, recv_buf, sizeof(recv_buf));/*讀取conn連接發送過來的數據*/if(ret < 0)error_print("read");else if(ret == 0){printf("client is close!\n");break;//父進程收到服務器端退出的信息(服務器Ctrl+C結束通信進程,read函數返回值為0,退出循環)}fputs(recv_buf,stdout);}kill(pid,SIGUSR1);/*父進程結束,也要向子進程發出一個信號告訴子進程終止接收,否則子進程會一直等待輸入*/}close(conn);close(sockfd);return 0; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

收到對端結束信息(NULL信息)的進程要向等待發送的進程發送一個結束通信的信號,回調函數處理使得等待輸入的進程結束,否則該進程會一直等待,直到有輸入(但此時的輸入已經沒有意義,所以應提早結束,而不是一直等待)。

三、測試結果:

這個不厚道的服務器結束了通信:


總結

以上是生活随笔為你收集整理的基于Linux的Socket编程之TCP全双工Server-Client聊天程序的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。