Linux系統程式設計(34)—— socket程式設計之TCP伺服器與客戶端的互動
前面幾篇中實現的client每次執行只能從命令列讀取一個字串發給伺服器,再從伺服器收回來,現在我們把它改成互動式的,不斷從終端接受使用者輸入並和server互動。
/* client.c */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
int main(int argc, char *argv[])
{
structsockaddr_in servaddr;
charbuf[MAXLINE];
intsockfd, n;
sockfd= Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family= AF_INET;
inet_pton(AF_INET,"127.0.0.1", &servaddr.sin_addr);
servaddr.sin_port= htons(SERV_PORT);
Connect(sockfd,(struct sockaddr *)&servaddr, sizeof(servaddr));
while(fgets(buf, MAXLINE, stdin) != NULL) {
Write(sockfd,buf, strlen(buf));
n= Read(sockfd, buf, MAXLINE);
if(n == 0)
printf("theother side has been closed.\n");
else
Write(STDOUT_FILENO,buf, n);
}
Close(sockfd);
return0;
}
編譯並執行server和client,看看是否達到了你預想的結果。
這時server仍在執行,但是client的執行結果並不正確。原因是什麼呢?仔細檢視server.c可以發現,server對每個請求只處理一次,應答後就關閉連線,client不能繼續使用這個連線傳送資料。但是client下次迴圈時又呼叫write發資料給server,write呼叫只負責把資料交給TCP傳送緩衝區就可以成功返回了,所以不會出錯,而server收到資料後應答一個RST段,client收到RST段後無法立刻通知應用層,只把這個狀態儲存在TCP協議層。client下次迴圈又呼叫write發資料給server,由於TCP協議層已經處於RST狀態了,因此不會將資料發出,而是發一個SIGPIPE訊號給應用層,SIGPIPE訊號的預設處理動作是終止程式,所以看到上面的現象。
為了避免client異常退出,上面的程式碼應該在判斷對方關閉了連線後break出迴圈,而不是繼續write。另外,有時候程式碼中需要連續多次呼叫write,可能還來不及呼叫read得知對方已關閉了連線就被SIGPIPE訊號終止掉了,這就需要在初始化時呼叫sigaction處理SIGPIPE訊號,如果SIGPIPE訊號沒有導致程式異常退出,write返回-1並且errno為EPIPE。
另外,我們需要修改server,使它可以多次處理同一客戶端的請求。
/* server.c */
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include "wrap.h"
#define MAXLINE 80
#define SERV_PORT 8000
int main(void)
{
structsockaddr_in servaddr, cliaddr;
socklen_tcliaddr_len;
intlistenfd, connfd;
charbuf[MAXLINE];
charstr[INET_ADDRSTRLEN];
inti, n;
listenfd= Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family= AF_INET;
servaddr.sin_addr.s_addr= htonl(INADDR_ANY);
servaddr.sin_port= htons(SERV_PORT);
Bind(listenfd,(struct sockaddr *)&servaddr, sizeof(servaddr));
Listen(listenfd,20);
printf("Acceptingconnections ...\n");
while(1) {
cliaddr_len= sizeof(cliaddr);
connfd= Accept(listenfd,
(structsockaddr *)&cliaddr, &cliaddr_len);
while(1) {
n= Read(connfd, buf, MAXLINE);
if(n == 0) {
printf("theother side has been closed.\n");
break;
}
printf("receivedfrom %s at PORT %d\n",
inet_ntop(AF_INET,&cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
for(i = 0; i < n; i++)
buf[i]= toupper(buf[i]);
Write(connfd,buf, n);
}
Close(connfd);
}
}
經過上面的修改後,客戶端和伺服器可以進行多次互動了。
相關文章
- TCP程式設計之服務端和客戶端的開發TCP程式設計服務端客戶端
- Linux Socket C語言網路程式設計:TCP SocketLinuxC語言程式設計TCP
- python網路-Socket之TCP程式設計(26)PythonTCP程式設計
- 系統程式設計-網路-tcp客戶端伺服器程式設計模型(續)、連線斷開、獲取連線狀態場景程式設計TCP客戶端伺服器模型
- 001 Rust 網路程式設計,實現 TCP 服務端和客戶端程式Rust程式設計TCP服務端客戶端
- Linux系統程式設計之程式介紹Linux程式設計
- socket程式設計在TCP中的應用程式設計TCP
- Windows Socket程式設計精華《TCP通訊伺服器》Windows程式設計TCP伺服器
- socket程式設計實現tcp伺服器_C/C++程式設計TCP伺服器C++
- 《Linux系統程式設計訓練營》8_Linux 終端與程式Linux程式設計
- Linux系統程式設計之匿名管道Linux程式設計
- Linux系統程式設計——特殊程式之孤兒程式Linux程式設計
- 【Linux系統程式設計】libevent庫實現簡易tcp伺服器Linux程式設計TCP伺服器
- shell程式設計之免互動程式設計
- 網路程式設計之socket程式設計
- SOCKET程式設計程式設計
- linux非阻塞式socket程式設計之select()用法Linux程式設計
- Linux作業系統之Shell程式設計Linux作業系統程式設計
- Linux系統程式設計之檔案IOLinux程式設計
- 基於TCP協議的Socket網路程式設計( )TCP協議程式設計
- (3)Tcp Socket程式設計的封裝類 TcpListener/TcpClientTCP程式設計封裝client
- 通過 Socket 實現 TCP 程式設計入門TCP程式設計
- PHP回顧之socket程式設計PHP程式設計
- 玩轉 PHP 網路程式設計全套之 socket stream 程式設計PHP程式設計
- Java Socket程式設計Java程式設計
- socket程式設計(1)程式設計
- Socket程式設計模型程式設計模型
- Python socket程式設計Python程式設計
- Linux系統程式設計之命名管道與共享記憶體Linux程式設計記憶體
- Java 網路程式設計(TCP程式設計 和 UDP程式設計)Java程式設計TCPUDP
- 【Linux】Linux系統程式設計入門Linux程式設計
- 基於TCP/UDP的Socket程式設計,HTTP/HTTPS協議TCPUDP程式設計HTTP協議
- 【Linux系統程式設計】libevent庫bufferevent與evconnlistenerLinux程式設計
- 伺服器端程式設計之 IO 模型伺服器程式設計模型
- Linux系統程式設計—有名管道Linux程式設計
- Linux系統程式設計基礎Linux程式設計
- Linux系統程式設計入門Linux程式設計
- Linux Socket C語言網路程式設計:UDP SocketLinuxC語言程式設計UDP
- Linux Socket C語言網路程式設計:Select SocketLinuxC語言程式設計