echo服务器(回显服务器)
轉載:https://blog.csdn.net/lanyan822/article/details/7679733
寫在文章前:
? ? ? ??這學習linux編程,也有一段時間了。雖然是一個人看書,琢磨。也想把自己看過的做一個總結,一步一步來,總有一天會質變的。不得不說,linux太博大精深了,里面需要學的東西太多了。
?
? ?echo服務器,可以看成 學習網絡編程的“hello world”。
? ?echo服務器,描述起來很簡單,服務端收到什么,就給客戶端發送什么。也就是這個簡單的程序,能夠讓你從中學到不少東西。多線程,多進程,I/O復用,信號處理這些都會遇到。
? ?第一個echo服務程序,不需要考慮各種問題,只要能夠完成功能就行。
? ?先來看看簡易流程圖
? ?
基于TCP Socket編程的流程圖類似,服務端主要是,創建socket、綁定IP和端口、監聽、接受連接、處理數據包、關閉連接。客戶端則主要是,創建socket、連接到服務器、發送請求、關閉連接。
?
? echo服務端代碼:
/* * File: Server.c* Author: root** Created on 2012年6月20日, 下午1:29*/#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h>#define SERVERIP "192.168.0.23" #define SERVERPORT 12345 #define MAXBUFFER 256int main(int argc, char** argv) {int serverFd, connfd,ret;socklen_t len;struct sockaddr_in serveraddr,clientaddr;char readBuf[MAXBUFFER]={0};char ip[40]={0};serverFd=socket(AF_INET,SOCK_STREAM,0);//創建socketif(serverFd < 0){printf("socket error:%s\n",strerror(errno));exit(-1);}bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family=AF_INET;serveraddr.sin_port=htons(SERVERPORT);inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);//將c語言字節序轉換為網絡字節序ret=bind(serverFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//綁定IP和端口if(ret!=0){close(serverFd);printf("bind error:%s\n",strerror(errno));exit(-1);}ret=listen(serverFd,5);//監聽if(ret!=0){close(serverFd);printf("listen error:%s\n",strerror(errno));exit(-1);}len=sizeof(clientaddr);bzero(&clientaddr,sizeof(clientaddr));while (1){connfd = accept(serverFd, (struct sockaddr *) &clientaddr, &len);//接受客戶端的連接printf("%s 連接到服務器 \n",inet_ntop(AF_INET,&clientaddr.sin_addr,ip,sizeof(ip)));if (serverFd < 0){printf("accept error : %s\n", strerror(errno));continue;}while((ret=read(connfd,readBuf,MAXBUFFER)))//讀客戶端發送的數據{write(connfd,readBuf,MAXBUFFER);//寫回客戶端bzero(readBuf,MAXBUFFER);}if(ret==0){printf("客戶端關閉連接\n"); }else{printf("read error:%s\n",strerror(errno));}close(connfd);}close(serverFd);return 0; }? ?echo 客戶端代碼
/* * File: Client.c* Author: root** Created on 2012年6月20日, 下午1:30*/#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <errno.h> #include <netinet/in.h> #include <arpa/inet.h>#define SERVERIP "192.168.0.23" #define SERVERPORT 12345 #define MAXBUFFER 256int main(int argc, char** argv) { int clientFd,ret;struct sockaddr_in serveraddr;char buf[MAXBUFFER];clientFd=socket(AF_INET,SOCK_STREAM,0);//創建socketif(clientFd < 0){printf("socket error:%s\n",strerror(errno));exit(-1);}bzero(&serveraddr,sizeof(serveraddr));serveraddr.sin_family=AF_INET;serveraddr.sin_port=htons(SERVERPORT);inet_pton(AF_INET,SERVERIP,&serveraddr.sin_addr);ret=connect(clientFd,(struct sockaddr *)&serveraddr,sizeof(serveraddr));//連接到服務器if(ret!=0){close(clientFd);printf("connect error:%s\n",strerror(errno));exit(-1);}while(1){bzero(buf,sizeof(buf));scanf("%s",buf);write(clientFd,buf,sizeof(buf));//寫數據bzero(buf,sizeof(buf));read(clientFd,buf,sizeof(buf));//讀數據printf("echo:%s\n",buf);}close(clientFd);return (EXIT_SUCCESS); }執行效果圖:
?
?
在這里需要注意一個問題:網絡字節序問題
內存中存儲字節有兩種方式,一種是小端字節序(低序字節儲存在起始地址),第二種是大端字節序(高位字節存儲在起始地址)。而且這兩種字節序都有在使用。當兩臺機器通信時,為了避免字節序之間轉換問題,系統提供了4個函數
#include<netinet/in.h> uint32_t ntohl (uint32_t __netlong) ; uint16_t ntohs (uint16_t __netshort); uint32_t htonl (uint32_t __hostlong); uint16_t htons (uint16_t __hostshort);用這個程序說明一下,ctrl-z,ctrl-c,ctrl-d的差別
ctrl-c 發送 SIGINT 信號給前臺進程組中的所有進程。常用于終止正在運行的程序。
? ? ?從上面那個圖可以看出,ctrl+c終止客戶端程序
ctrl-z 發送 SIGTSTP 信號給前臺進程組中的所有進程,常用于掛起一個進程。
? ? ctrl+z,將程序掛起。fg命令重新啟動前臺被中斷的任務,bg命令把被中斷的任務放在后臺執行.
ctrl-d 不是發送信號,而是表示一個特殊的二進制值,表示 EOF。
程序不足之處:
?
總結
以上是生活随笔為你收集整理的echo服务器(回显服务器)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入理解阻塞socket和非阻塞sock
- 下一篇: 什么是死锁?死锁产生的四个必要条件?如何