生活随笔
收集整理的這篇文章主要介紹了
基于Linux的socket编程模板
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
??? 在網絡編程編程中,我們經常會遇到這樣一種C/S架構,服務器端(Server)監聽客戶端(Client)發送過來的命令,然后解析該命令,并做對應的處理,最后返回處理結果(例如成功或者失敗及原因)給客戶端。
??? 最近,在Linux下做網絡編程,涉及的就是上面的這種需求,簡單地整理了下自己的代碼,分享在這里吧,供初學者參考。
??? 首先說一下編程思路吧。
??? 在這種情況客戶端必須實現的的接口有:連接服務器、發送、斷開連接。
??? 服務器端,有一個主線程,用于監聽客戶端的連接請求,一旦有新客戶端連接,則創建一個新的socket及線程專門服務這個客戶端。這個服務線程專門監聽該客戶端的命令,并且解析命令進行服務器,直到客戶端斷開連接或者發送關閉連接的命令。
??? 另外,需要涉及一個通信協議,約定命令的包頭、命令的識別碼、命令的參數。
??? 思路就說到這兒了,下面的相關代碼,附件中有完整的代碼,包含了Makefile文件。
一、通信協議設計
???????????????????#ifndef?COMMAND_H_ ?#define?COMMAND_H_ ??typedef?unsigned?char?uint8_t; ???#define?TCP_CMD_HEADER_LEN?8 ???static?uint8_t?TCP_CMD_HEADER_STR[TCP_CMD_HEADER_LEN]?=?{?0xAA,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xFF?}; ???#define?MAX_USER_NAME_LEN?20 ???typedef?enum?_ServerCMD ?{ ?????CMD_SAVE_USER_NAME,??????????????CMD_SAVE_USER_AGE,????????????}ServerCMD; ???typedef?enum?_ReturnCMD ?{ ?????DVS_RETURN_SUCCESS?=?0,?????? ?????DVS_RETURN_FAIL,????????????? ?????DVS_RETURN_TIMEOUT,?????????? ?????DVS_RETURN_INVLID_HEADER,???? ?????DVS_RETURN_INVLID_CMD,??????? ?????DVS_RETURN_INVLID_PRM,??????? ??}ReturnCMD; ???#pragma?pack(?push,?1?) ???typedef?struct?_ServerPack? ?{ ??????????uint8_t?cmdHeader[TCP_CMD_HEADER_LEN]; ???????????ServerCMD?serverCMD; ???????????union?????{ ????????????????????struct???????????{ ????????????????????????????char?username[MAX_USER_NAME_LEN]; ????????????}UserName; ???????????????????????struct???????????{ ????????????????????????????int?userage; ????????????}UserAge; ??????}Parameters; ??}ServerPack; ???typedef?struct?_ReturnPack ?{ ??????????uint8_t?cmdHeader[TCP_CMD_HEADER_LEN]; ???????????ReturnCMD?returnCMD; ??}ReturnPack; ??#pragma?pack(?pop?) ??#define?SERVER_PACK_LEN?sizeof(ServerPack) ?#define?RETURN_PACK_LEN?sizeof(ReturnPack) ??#endif?//?COMMAND_H_ ? 二、客戶端代碼
???????????????????#include?<stdio.h> ?#include?<sys/types.h> ?#include?<sys/socket.h> ?#include?<netinet/in.h> ?#include?<unistd.h> ?#include?<errno.h> ?#include?<string.h> ?#include?"client.h" ???int?g_hSocket; ??int?connect_server(?char?*destIp,?int?destPort?) ?{ ?????int?result; ?????struct?sockaddr_in?address; ???????????g_hSocket?=?socket(AF_INET,SOCK_STREAM,0); ???????????address.sin_family?=?AF_INET; ???????????address.sin_addr.s_addr?=?inet_addr(?destIp?); ?????address.sin_port????????=?htons(destPort); ????? ??????????result?=?connect(g_hSocket,(struct?sockaddr?*)&address,sizeof(address)?); ?????if(?result?==?-1?) ?????{ ?????????printf("[tcp?client]?can't?connect?server?!\n"); ???????????return?-1; ?????} ????? ?????return?0; ?} ??int?close_connect() ?{ ?????printf("close?connect?with?server?!\n?"); ??????close(g_hSocket); ??????return?0; ?} ??int?send_cmd(?ServerPack?sPack?) ?{ ?????int?recvBytes?=?0; ?????int?sendBytes?=?0; ?????ReturnPack?rPack; ???????????memcpy(sPack.cmdHeader,TCP_CMD_HEADER_STR,TCP_CMD_HEADER_LEN); ???????????while(1) ?????{ ?????????sendBytes?=?send(g_hSocket,(uint8_t?*)&sPack,SERVER_PACK_LEN,0); ????????? ?????????if(?sendBytes?==?SERVER_PACK_LEN?) ?????????{ ?????????????printf("successfully?send?bytes?%d\n",SERVER_PACK_LEN); ?????????????break; ?????????} ?????????else?if(?sendBytes?<=?0?&&?errno?!=?EINTR?&&?errno?!=?EWOULDBLOCK?&&?errno?!=?EAGAIN) ?????????{ ?????????????printf("disconnected?or?other?errors!\n"); ???????????????return?-1; ?????????} ?????????else?????????{ ?????????????continue; ?????????} ?????} ???????????while(1) ?????{ ?????????recvBytes?=?recv(g_hSocket,(uint8_t?*)&rPack,RETURN_PACK_LEN,0); ????????????if(?recvBytes?==?RETURN_PACK_LEN?) ???????????{ ???????????????break; ???????????} ???????????else?if(?recvBytes?<=0?&&?errno?!=?EINTR?&&?errno?!=?EWOULDBLOCK?&&?errno?!=?EAGAIN?) ???????????{ ???????????????printf("disconnected?or?error?occur!\n?close?the?socket!\n"); ???????????????return?-1; ???????????} ???????????else???????????{? ???????????????continue; ???????????} ?????} ???????????if?(?memcmp(?rPack.cmdHeader,?TCP_CMD_HEADER_STR,?TCP_CMD_HEADER_LEN?)?!=?0?) ?????{ ?????????printf("return?pack?header?errror!\n"); ?????????return?-2; ?????} ???????????if(?rPack.returnCMD?!=?DVS_RETURN_SUCCESS?) ?????{ ?????????printf("return?status?:?fail!\n"); ?????????return?-3; ?????} ????? ?????return?0;???? ?} ??? 三、服務器主線程代碼
???????????????????#include?<sys/types.h> ?#include?<sys/socket.h> ?#include?<stdio.h> ??#include?<netinet/in.h> ?#include?<unistd.h> ??#include?"serverIf.h" ??int?g_hServerSocket; ??int?open_port(?int?localport?) ?{ ?????int?result; ?????int?clientSocket,client_len; ??????struct?sockaddr_in?server_addr; ?????struct?sockaddr_in?client_addr; ???????????g_hServerSocket?=?socket(AF_INET,SOCK_STREAM,0); ???????????server_addr.sin_family?=?AF_INET; ?????server_addr.sin_addr.s_addr?=?htonl(INADDR_ANY); ?????server_addr.sin_port?=?htons(localport); ??????result?=?bind(g_hServerSocket,(struct?sockaddr?*)&server_addr,sizeof(server_addr)?); ?????if(?result?!=?0?)? ?????{ ????????????printf("[tcp?server]?bind?error!\n?");???? ????????????return?-1; ?????} ???????????result?=?listen(g_hServerSocket,5); ?????if(?result?!=?0?) ?????{ ?????????printf("[tcp?server]?listen?error!\n?"); ???????????return?-1; ?????} ?? // 注: ServerEnv用于給服務線程傳參,定義于serverIf.h中????ServerEnv?env; ?????while(1) ?????{ ???????????client_len?=?sizeof(client_addr); ???????????clientSocket?=?accept(g_hServerSocket,(struct?sockaddr?*)&client_addr,&client_len?); ????????????if(?clientSocket?<?0?) ???????????{ ???????????????printf("[tcp?server]?accept?error!\n"?); ???????????????return?-1; ???????????} ???????????? ???????????env.m_hSocket?=?clientSocket; ??????????? ??????????????????????add_new_tcp_process_thr(&env); ?????} ??????return?0; ?} ??int?close_port() ?{ ?????printf("close?server?port,?stop?listen!\n"); ??????close(g_hServerSocket); ??????return?0; ?} ? 四、服務器端服務線程代碼
???????????????????#include?"serverIf.h" ?#include?"../include/Command.h" ?#include?<sys/socket.h> ?#include?<stdio.h> ?#include?<string.h> ?#include?<errno.h> ??int?add_new_tcp_process_thr(?ServerEnv?*envp?) ?{ ?????pthread_t?tcpThr; ??????if(?pthread_create(?&tcpThr,NULL,tcpServerThrFxn,envp?)?) ?????{ ?????????printf("tcp?thread?create?fail!\n"); ?????????return?-1; ?????} ??????printf("tcp?thread?has?been?created!\n"); ??????return?0; ?} ??int?save_user_name(?char?*?pUsername?) ?{ ?????printf("ok,user?name?saved,username=%s\n",pUsername); ??????return?0; ?} ??int?save_user_age(?int?age?) ?{ ?????printf("ok,user?age?saved,userage=%d\n",age); ??????return?0; ?} ??void?*?tcpServerThrFxn(?void?*?arg?) ?{ ?????ServerEnv?*?envp?=?(ServerEnv?*)arg; ?????int?socketfd?=?envp->m_hSocket; ?????int?returnBytes; ??????ServerPack?sPack; ?????ReturnPack?rPack; ?????memcpy(rPack.cmdHeader,TCP_CMD_HEADER_STR,TCP_CMD_HEADER_LEN); ??????while(1) ?????{ ??????????????????????returnBytes?=?recv(socketfd,(uint8_t?*)&sPack,SERVER_PACK_LEN,0); ????????? if(?returnBytes?==?SERVER_PACK_LEN?) ???????????{ ??????????????????????????} ???????????else?if(?returnBytes?<=?0?&&?errno?!=?EINTR?&&?errno?!=?EWOULDBLOCK?&&?errno?!=?EAGAIN?) ???????????{ ???????????????printf("disconnected?or?error?occur!?errno=%d\n?",errno); ???????????????break; ???????????} ???????????else???????????{ ???????????????continue; ???????????} ????????? ????????????if?(?memcmp(?sPack.cmdHeader,?TCP_CMD_HEADER_STR,?TCP_CMD_HEADER_LEN?)?!=?0?) ???????????{ ???????????????rPack.returnCMD?=?DVS_RETURN_INVLID_HEADER; ???????????????????????????????returnBytes?=?send(socketfd,(uint8_t?*)&rPack,RETURN_PACK_LEN,0?)?; ???????????????if(?returnBytes?<?RETURN_PACK_LEN) ???????????????{ ???????????????????printf("send?error!\n"); ?????????????????????continue; ???????????????} ???????????} ???????????????????????rPack.returnCMD?=?DVS_RETURN_SUCCESS; ????????? switch(?sPack.serverCMD?) ???????????{ ???????????case?CMD_SAVE_USER_NAME: ????????????????{?? ??????????????????????if(?save_user_name(sPack.Parameters.UserName.username)?!=?0) ??????????????????????{ ??????????????????????????rPack.returnCMD?=?DVS_RETURN_FAIL; ??????????????????????} ????????????????} ???????????break; ???????????case?CMD_SAVE_USER_AGE: ???????????????{ ????????????????????if(?save_user_age(sPack.Parameters.UserAge.userage)?!=?0) ??????????????????????{? ??????????????????????????rPack.returnCMD?=?DVS_RETURN_FAIL; ??????????????????????} ???????????????} ???????????break; ???????????default: ??????????????{?? ????????????????????rPack.returnCMD?=?DVS_RETURN_INVLID_CMD; ??????????????} ???????????break; ???????????}? ????? ??????????????????????returnBytes?=?send(socketfd,(uint8_t?*)&rPack,RETURN_PACK_LEN,0?); ???????????if(?returnBytes?<?RETURN_PACK_LEN?) ???????????{ ?????????????printf("send?error!\n"); ?????????????continue;??????? ???????????} ?????} ??????printf("close?session?socket!"); ???????????close(socketfd); ??????return?(void*)0; ?} ? 五、客戶端測試代碼
???????????????????#include?<stdio.h> ??#include?"client.h" ??#define?LOCAL_IP_STR?"127.0.0.1" ?#define?DEST_IP_STR??"192.201.0.8" ?#define?DEST_PORT????8000 ??int?main(int?argc,?char?**argv) ?{ ?????int?i?=0; ??????printf("tcp?test?start!\n"); ??????if(?connect_server(DEST_IP_STR,DEST_PORT)?!=?0) ?????{ ?????????return?-1; ?????} ??????ServerPack?sPack; ?????sPack.serverCMD?=?CMD_SAVE_USER_AGE; ?????sPack.Parameters.UserAge.userage?=?20; ??????if(?send_cmd(sPack)?==?-1?) ???????{ ???????????printf("send?cmd?fail!\n"); ???????} ??????getchar(); ?????getchar(); ??????close_connect(); ??????printf("tcp?test?pass!\n"); ??????return?0; ?} ? 六、服務器端測試代碼
???????????????????#include?<stdio.h> ??#include?"server.h" ?#include?"serverIf.h" ??#define?LOCAL_PORT?8000 ??int?main(int?argc,?char?**argv) ?{ ?????printf("tcp?test?start!\n"); ??????if(?open_port(LOCAL_PORT)?!=?0) ?????{ ?????????return?-1; ?????} ??????close_port(); ??????printf("?close?port?!\n"); ??????return?0; ?} ? 七、總結和說明
??? 本文后面的附件中有完整的代碼,歡迎下載使用。編譯方法,把代碼文件夾都拷貝到linux下,在本代碼文件夾的根目錄下,運行make,即可生成對應的可執行文件。在運行測試程序的時候,請先執行server.out,然后執行client.out
??? 另外,如果需要轉載本文或者代碼,請注明出處,本代碼來自ticktick的博客:http://ticktick.blog.51cto.com?謝謝。
??? 如果發現本代碼的任何bug或者有任何建議,歡迎留言或者來信交流。
轉載于:https://blog.51cto.com/ticktick/645523
總結
以上是生活随笔為你收集整理的基于Linux的socket编程模板的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。