Linux网络编程实例分析
最近由于工作原因需要溫習(xí)一下Linux網(wǎng)絡(luò)編程的部分基礎(chǔ)知識,因此對之前寫的Socket網(wǎng)絡(luò)通信的代碼進(jìn)行了進(jìn)一步優(yōu)化和拓展,在不關(guān)閉一次Socket連接的基礎(chǔ)上,對服務(wù)端加入循環(huán)讀寫的功能,同時加入連接檢測函數(shù),防止客戶端意外斷開(例如Ctrl + C殺掉客戶端程序),服務(wù)端程序陷入死循環(huán)。增加客戶端退出命令(服務(wù)端亦接收此命令),增加客戶端執(zhí)行服務(wù)端程序的命令,這里主要用到了strncasecmp和system函數(shù)。
最近看同事代碼時看到了C程序中使用回調(diào)函數(shù),為了加強(qiáng)對回調(diào)函數(shù)的理解,于是簡單嘗試想用回調(diào)函數(shù)調(diào)用系統(tǒng)read和write函數(shù),但后來發(fā)現(xiàn)第三個參數(shù)不兼容,編譯時告警(雖然可以通便編譯,正常運(yùn)行),但最后只能作罷,將read函數(shù)和write的回調(diào)函數(shù)分別進(jìn)行了編輯。關(guān)于回調(diào)函數(shù),這篇博文寫的比較淺顯易懂,可以作為入門講解:
C語言回調(diào)函數(shù)詳解 - 江召偉 - 博客園
服務(wù)端程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include<netinet/tcp.h>#define MAX_LINE 100
/* 處理函數(shù),用于將大寫字符轉(zhuǎn)換為小寫字符。參數(shù)為需要轉(zhuǎn)換的字符串 */
void upper_to_lower(char *str)
{if (str == NULL)				 /* 空串 */return;for (; *str != '\0'; str++){if(*str >= 'A' && *str <= 'Z') /* 判斷字符并進(jìn)行轉(zhuǎn)換,也可以使用庫函數(shù) */*str = *str -'A' + 'a'; }
}/* 判斷接收的字符串是否為定義的命令,若是則執(zhí)行相應(yīng)的操作 */
int msg_to_cmd(char *str)
{int len = strlen(str);if (strncasecmp(str, "excute", len) == 0){cmd_excute("~/linux/helloWorld"); //執(zhí)行指定目錄下的helloWorld應(yīng)用程序return -1;}else if (strncasecmp(str ,"quit", len) == 0){return 1;}else{}return 0;
}/* 封裝Linux系統(tǒng)命令調(diào)用函數(shù)system */
int cmd_excute(char *cmd)
{printf("Excute command: %s\n",cmd);system(cmd);return 0;
}/*TCP連接檢測函數(shù),用于檢測客戶端是否異常斷開,防止服務(wù)端socket程序崩潰*/
int connect_detect(int fd)
{struct tcp_info info;int len = sizeof(info);if (fd <= 0) return 0;getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&len);/*if (info.tcpi_state == TCP_ESTABLISHED) */if (info.tcpi_state == 1)  // Is connectedreturn 0;else  // Losedreturn 1;
}/*封裝write函數(shù)的回調(diào)函數(shù),增加打印功能 */
int write_callback(int *fd, const void *buf_ptr, size_t len, ssize_t (*wr)(int, const void*, size_t))
{int res = 0;res = wr(*fd, buf_ptr, len);if (res != -1){printf("Write message succeed !\n");}else{printf("Write message failed !\n");}//res = wr(*fd, buf_ptr, len);//printf("Receive Message: %s\n", buf_ptr);return res;
}/* 由于read函數(shù)的第二個參數(shù)非const,因此無法與write函數(shù)使用同一回調(diào)函數(shù),否則編譯器會報(bào)警 */
int read_callback(int *fd, void *buf_ptr, size_t len, ssize_t (*rd)(int, void*, size_t))
{int count = 0;count = rd(*fd, buf_ptr, len);printf("Receive message: %s\n", buf_ptr);return count;
}int main(void)
{struct sockaddr_in sin;struct sockaddr_in cin;int l_fd;int c_fd;socklen_t len;char buf[MAX_LINE];					/* 存儲傳送內(nèi)容的緩沖區(qū) */char addr_p[INET_ADDRSTRLEN];		/* 存儲客戶端地址的緩沖區(qū) */int port = 80000;					/* 端口號,使用8000 */bzero(&sin, sizeof(sin));			/* 清空地址結(jié)構(gòu) */sin.sin_family      = AF_INET;			/* 使用IPv4通信域 */sin.sin_addr.s_addr = INADDR_ANY;	/* 服務(wù)器可以接受任意地址 */sin.sin_port        = htons(port);		/* 端口號轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序 */l_fd                = socket(AF_INET, SOCK_STREAM, 0); /* 創(chuàng)立套接字,使用TCP協(xié)議 */bind(l_fd, (struct sockaddr*) &sin, sizeof(sin)); /* 將地址和套接字綁定 */listen(l_fd, 10);						/* 開始監(jiān)聽連接請求 *///printf("Waiting ...\n");while (printf("Waiting ...\n")){/* 服務(wù)器程序多半是死循環(huán) *//* 接受連接請求,從此函數(shù)中返回后就可以開始通信了 */int cmd_result = 0;//printf("Waiting ...\n");c_fd = accept(l_fd, (struct sockaddr*) &cin, &len);while (1){/*if (recv(c_fd, buf, MAX_LINE, 0) == -1){break;}count = read(c_fd, buf, MAX_LINE);*/	/* 讀取客戶端傳來的信息 *///inet_ntop(AF_INET, &cin.sin_addr, addr_p, sizeof(addr_p));/* 將客戶端地址轉(zhuǎn)換為字符串 *///printf("client IP is %s, port is %d\n", addr_p, ntohs(cin.sin_port)); 		/* 打印客戶端地址和端口號 *///printf("Receive Message: %s\n", buf);			/* 打印客戶端發(fā)送過來的字符串 */int wrt = 0;read_callback(&c_fd, buf, MAX_LINE, read);upper_to_lower(buf);						/* 調(diào)用大小寫轉(zhuǎn)換函數(shù) */					cmd_result =  msg_to_cmd(buf);if( (cmd_result == 1 ) || connect_detect(c_fd) )           /*若客戶端執(zhí)行quit命令正常退出或者客戶端異常斷開則跳出本次循環(huán)*/{printf("End communication !\n");break;}else if ( cmd_result == -1 ){char *p = "Command excuted succeed !";wrt = write_callback(&c_fd, p, 25, write);}else{wtr = write_callback(&c_fd, buf, strlen(buf)+1, write); /* 將轉(zhuǎn)換后的字串發(fā)給客戶端 */}if ( wrt == -1 ){printf("End communication !\n");break;}}	close(c_fd);			/* 通信結(jié)束,關(guān)閉套接字,準(zhǔn)備下一次通信 */}if(close(l_fd) == -1){		/* 通信結(jié)束,關(guān)閉套接字,準(zhǔn)備下一次通信 */perror("Fail to close");exit(1);}return 0; /* 不應(yīng)該執(zhí)行到這里 */
}在服務(wù)端目錄~/linux/下面創(chuàng)建并編譯helloWorld.c文件,在監(jiān)聽程序中由system函數(shù)執(zhí)行運(yùn)行helloWorld程序。
客戶端程序:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#define MAX_LINE 100int cmd_excute(char *cmd)
{printf("Excute command: %s\n",cmd);system(cmd);return 0;
}int msg_to_cmd(char *str)
{	int len = strlen(str);if (strncasecmp(str ,"quit", len) == 0){return 1;}else if (strncasecmp(str, "ls", len) == 0){cmd_excute("ls ./");return 2;}	else if(strncasecmp(str ,"help", len) == 0){printf("Command list:\n");printf("ls --display directory\n");printf("quit --End communication\n");return 3;}else{}return 0;
}int main(void)
{struct sockaddr_in sin;char   msg[100];int    l_fd;char   buf[MAX_LINE];                                     /* 存儲傳送內(nèi)容的緩沖區(qū) */int    port = 80000;                                       /* 端口號,使用8000 */bzero(&sin, sizeof(sin));                       /* 清空地址結(jié)構(gòu) */sin.sin_family = AF_INET;                       /* 使用IPv4通信域 */inet_pton(AF_INET,"10.17.39.166", &sin.sin_addr);sin.sin_port   = htons(port);             /* 端口號轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序 */l_fd           = socket(AF_INET, SOCK_STREAM, 0); /* 創(chuàng)立套接字,使用TCP協(xié)議 */connect(l_fd, (const struct sockaddr*)&sin, sizeof(sin));while(1){int wrt  = -1;int res  = 0;int flag = 0;
//        	l_fd = socket(AF_INET, SOCK_STREAM, 0); /* 創(chuàng)立套接字,使用TCP協(xié)議 */
//	        connect(l_fd, (const struct sockaddr*)&sin, sizeof(sin));printf("Input Message:\n");scanf("%s", msg);res = msg_to_cmd(msg);switch(res){case 0:wrt = write(l_fd, msg, strlen(msg)+1);sleep(3);if ( wrt != -1 ){printf("Write message succeed !\n");}else{printf("Write message failed !\n");flag = 1;break;}read(l_fd, buf, MAX_LINE);printf("Receive messaege from server:\n%s\n", buf);break;case 1:res = write(l_fd, msg, strlen(msg)+1);sleep(3);close(l_fd);flag = 1;break;case 2:break;default:break;}if (flag){break;}
//		printf("Socket state: %#X\n", l_fd);}if(close(l_fd) == -1){          /* 通信結(jié)束,關(guān)閉套接字,準(zhǔn)備下一次通信 */perror("Fail to close !");}return 0;
}
總結(jié)
以上是生活随笔為你收集整理的Linux网络编程实例分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Ubuntu解决开机屏幕默认亮度偏低问题
- 下一篇: 真三国无双霸官职有什么用?
