#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <string.h> // menset 函數 #include <stdlib.h> // exit 函數 #include<netinet/in.h> // struct sockaddr_in 結構類型 #include<arpa/inet.h> // inet_ntoa 函數 #include <unistd.h> // close 函數 int tcpServerInit( void ); void main() { //2017年11月28日11:18:18,suozhang,add printf("2017年11月28日11:18:39,hello,world!\r\n"); tcpServerInit(); } int tcpServerInit( void ) { // AF_INET : IPV4網絡協議 // SOCK_STREAM : 提供雙向連續且可信賴的數據流,即TCP,支持OOB機制,在所有數據傳輸前必須使用connect()來建立連接狀態 // 0 : 用來指定socket所使用的傳輸協議編號,通常此參考不用管他,設為0即可 int tcpServerSocket = socket(AF_INET, SOCK_STREAM, 0); if(tcpServerSocket < 0) { // 創建 socket 失敗 // perror(s) 用來將上一個函數發生錯誤的原因輸出到標准設備(stderr)。 perror("new tcpServerSocket error is "); //exit(-1)表示程序異常退出 exit(-1); } //定義 服務器 IP地址和端口號 結構體變量 struct sockaddr_in tcpServerAddr; //將結構體清空 memset(&tcpServerAddr,0,sizeof(tcpServerAddr)); tcpServerAddr.sin_family = AF_INET; // AF_INET : IPV4網絡協議 // htons() 函數用來將 16位 類型 轉換為 網絡字符順序 tcpServerAddr.sin_port = htons(9527);// 綁定端口號為9527,通常是大於1024的一個值 // inet_addr() 函數用來將IP地址字符串轉換成網絡所使用的二進制數字 tcpServerAddr.sin_addr.s_addr = inet_addr("10.1.51.53");// 這里可填寫 INADDRY_ANY 表示服務器自動填充本機IP地址 if( bind(tcpServerSocket, (struct sockaddr*)&tcpServerAddr, sizeof(tcpServerAddr)) < 0 ) { //綁定失敗 perror("bind tcpServerSocket err is "); close( tcpServerSocket ); //綁定失敗,因此關閉創建的 socket //exit(-1)表示程序異常退出 exit(-1); } printf("TCP server ip: %s and port: %d.\r\n", inet_ntoa(tcpServerAddr.sin_addr), ntohs(tcpServerAddr.sin_port)); //監聽socket ,第二個參數規定了內核應該為相應套接口排隊的最大連接個數。 if( listen(tcpServerSocket, 5) < 0) { //監聽失敗 perror("listen tcpServerSocket err is "); close( tcpServerSocket ); //綁定失敗,因此關閉創建的 socket //exit(-1)表示程序異常退出 exit(-1); } struct sockaddr_in tcpClientAddr; //將結構體清空 memset(&tcpClientAddr,0,sizeof(tcpClientAddr)); socklen_t tcpClientAddrLen = sizeof(struct sockaddr); // accept() 函數 接受遠程計算機的連接請求,建立與客戶機之間的通信連接。 // 服務器處於監聽狀態時,如果某時刻獲得客戶機的連接請求,此時並不是立即處理這個請求, // 而是將這個請求放在等待隊列中,當系統空閑時再處理客戶機的連接請求。 // 重點: 當 accept() 函數接受一個連接時,會返回一個新的socket標識符, // 以后的數據傳輸和讀取就要通過這個新的socket編號來處理, // 原來參數的socket(這里指 tcpServerSocket )也可以繼續使用,繼續監聽其他客戶機的連接請求 // 因此 服務器跟一個客戶端連接成功,就會產生兩個套接字,一個當初自己創建的,一個 accept() 創建的 int tcpClientSocket = accept(tcpServerSocket, (struct sockaddr*)&tcpClientAddr, &tcpClientAddrLen); if( tcpClientSocket < 0 ) { perror("accept tcpServerSocket err is "); close( tcpServerSocket ); //綁定失敗,因此關閉創建的 socket //exit(-1)表示程序異常退出 exit(-1); } else { printf("connected with ip: %s and port: %d.\r\n", inet_ntoa(tcpClientAddr.sin_addr), ntohs(tcpClientAddr.sin_port)); } char buf[1024] ={ 0 }; for( ;; ) { if( recv( tcpClientSocket, buf, sizeof( buf ),0 ) < 0 ) { perror("recv tcpClientSocket err is "); close( tcpServerSocket ); //綁定失敗,因此關閉創建的 socket close( tcpClientSocket ); //綁定失敗,因此關閉創建的 socket //exit(-1)表示程序異常退出 exit(-1); } printf("接收的數據是:%s.\r\n",buf); //將接收的數據發回客戶端 if( send( tcpClientSocket,buf,strlen(buf),0 ) < 0 ) { perror("send tcpClientSocket err is "); close( tcpServerSocket ); //綁定失敗,因此關閉創建的 socket close( tcpClientSocket ); //綁定失敗,因此關閉創建的 socket //exit(-1)表示程序異常退出 exit(-1); } //將接收緩沖區清空 memset(buf,0,sizeof(buf)); } }