在网络编程编程中,我们经常会遇到这样一种C/S架构,服务器端(Server)监听客户端(Client)发送过来的命令,然后解析该命令,并做对应的处理,最后返回处理结果(例如成功或者失败及原因)给客户端。

最近,在Linux下做网络编程,涉及的就是上面的这种需求,简单地整理了下自己的代码,分享在这里吧,供初学者参考。

首先说一下编程思路吧。

在这种情况客户端必须实现的的接口有:连接服务器、发送、断开连接。

服务器端,有一个主线程,用于监听客户端的连接请求,一旦有新客户端连接,则创建一个新的socket及线程专门服务这个客户端。这个服务线程专门监听该客户端的命令,并且解析命令进行服务器,直到客户端断开连接或者发送关闭连接的命令。

另外,需要涉及一个通信协议,约定命令的包头、命令的识别码、命令的参数。

思路就说到这儿了,下面的相关代码,附件中有完整的代码,包含了Makefile文件。

一、通信协议设计

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    Command.h
  7. /// @brief   命令包声明文件
  8. ///
  9. /// 定义各种TCP命令包以及相关结构体
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #ifndef COMMAND_H_
  20. #define COMMAND_H_
  21. typedef unsigned char uint8_t;
  22. // TCP CMD header len
  23. #define TCP_CMD_HEADER_LEN 8
  24. // CMD header
  25. static uint8_t TCP_CMD_HEADER_STR[TCP_CMD_HEADER_LEN] = { 0xAA,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xFF };
  26. // max user name len
  27. #define MAX_USER_NAME_LEN 20
  28. // server cmd struct
  29. typedef enum _ServerCMD
  30. {
  31. CMD_SAVE_USER_NAME,         // save user name
  32. CMD_SAVE_USER_AGE,          // save user age
  33. }ServerCMD;
  34. // return cmd
  35. typedef enum _ReturnCMD
  36. {
  37. DVS_RETURN_SUCCESS = 0,
  38. DVS_RETURN_FAIL,
  39. DVS_RETURN_TIMEOUT,
  40. DVS_RETURN_INVLID_HEADER,
  41. DVS_RETURN_INVLID_CMD,
  42. DVS_RETURN_INVLID_PRM,
  43. }ReturnCMD;
  44. // 1bytes aligning
  45. #pragma pack( push, 1 )
  46. // server pack from client
  47. typedef struct _ServerPack
  48. {
  49. // cmd header
  50. uint8_t cmdHeader[TCP_CMD_HEADER_LEN];
  51. // command id
  52. ServerCMD serverCMD;
  53. // cmd param
  54. union
  55. {
  56. // save user name
  57. struct
  58. {
  59. // user name
  60. char username[MAX_USER_NAME_LEN];
  61. }UserName;
  62. // save user age
  63. struct
  64. {
  65. // user age
  66. int userage;
  67. }UserAge;
  68. }Parameters;
  69. }ServerPack;
  70. // return pack from server
  71. typedef struct _ReturnPack
  72. {
  73. // cmd header
  74. uint8_t cmdHeader[TCP_CMD_HEADER_LEN];
  75. // return cmd
  76. ReturnCMD returnCMD;
  77. }ReturnPack;
  78. #pragma pack( pop )
  79. #define SERVER_PACK_LEN sizeof(ServerPack)
  80. #define RETURN_PACK_LEN sizeof(ReturnPack)
  81. #endif // COMMAND_H_

二、客户端代码

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    client.c
  7. /// @brief   tcp客户端代码
  8. ///
  9. /// 实现tcp客户端的相关接口
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #include <stdio.h>
  20. #include <sys/types.h>
  21. #include <sys/socket.h>
  22. #include <netinet/in.h>
  23. #include <unistd.h>
  24. #include <errno.h>
  25. #include <string.h>
  26. #include "client.h"
  27. // socket handle
  28. int g_hSocket;
  29. int connect_server( char *destIp, int destPort )
  30. {
  31. int result;
  32. struct sockaddr_in address;
  33. // create a socket
  34. g_hSocket = socket(AF_INET,SOCK_STREAM,0);
  35. // set server addr
  36. address.sin_family = AF_INET;
  37. // use server ip and listen port to connect
  38. address.sin_addr.s_addr = inet_addr( destIp );
  39. address.sin_port        = htons(destPort);
  40. // connect tcp server
  41. result = connect(g_hSocket,(struct sockaddr *)&address,sizeof(address) );
  42. if( result == -1 )
  43. {
  44. printf("[tcp client] can't connect server !\n");
  45. return -1;
  46. }
  47. return 0;
  48. }
  49. int close_connect()
  50. {
  51. printf("close connect with server !\n ");
  52. close(g_hSocket);
  53. return 0;
  54. }
  55. int send_cmd( ServerPack sPack )
  56. {
  57. int recvBytes = 0;
  58. int sendBytes = 0;
  59. ReturnPack rPack;
  60. // add cmd header
  61. memcpy(sPack.cmdHeader,TCP_CMD_HEADER_STR,TCP_CMD_HEADER_LEN);
  62. // send cmd
  63. while(1)
  64. {
  65. sendBytes = send(g_hSocket,(uint8_t *)&sPack,SERVER_PACK_LEN,0);
  66. if( sendBytes == SERVER_PACK_LEN )
  67. {
  68. printf("successfully send bytes %d\n",SERVER_PACK_LEN);
  69. break;
  70. }
  71. else if( sendBytes <= 0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN)
  72. {
  73. printf("disconnected or other errors!\n");
  74. return -1;
  75. }
  76. else
  77. {
  78. continue;
  79. }
  80. }
  81. // recv process result from server
  82. while(1)
  83. {
  84. recvBytes = recv(g_hSocket,(uint8_t *)&rPack,RETURN_PACK_LEN,0);
  85. if( recvBytes == RETURN_PACK_LEN )
  86. {
  87. break;
  88. }
  89. else if( recvBytes <=0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN )
  90. {
  91. printf("disconnected or error occur!\n close the socket!\n");
  92. return -1;
  93. }
  94. else
  95. {
  96. continue;
  97. }
  98. }
  99. // check header
  100. if ( memcmp( rPack.cmdHeader, TCP_CMD_HEADER_STR, TCP_CMD_HEADER_LEN ) != 0 )
  101. {
  102. printf("return pack header errror!\n");
  103. return -2;
  104. }
  105. // get return status
  106. if( rPack.returnCMD != DVS_RETURN_SUCCESS )
  107. {
  108. printf("return status : fail!\n");
  109. return -3;
  110. }
  111. return 0;
  112. }

三、服务器主线程代码

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    server.c
  7. /// @brief   tcp服务器主线程代码
  8. ///
  9. /// 实现tcp服务端监听线程相关函数
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #include <sys/types.h>
  20. #include <sys/socket.h>
  21. #include <stdio.h>
  22. #include <netinet/in.h>
  23. #include <unistd.h>
  24. #include "serverIf.h"
  25. int g_hServerSocket;
  26. int open_port( int localport )
  27. {
  28. int result;
  29. int clientSocket,client_len;
  30. struct sockaddr_in server_addr;
  31. struct sockaddr_in client_addr;
  32. // create a socket obj for server
  33. g_hServerSocket = socket(AF_INET,SOCK_STREAM,0);
  34. // bind tcp port
  35. server_addr.sin_family = AF_INET;
  36. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  37. server_addr.sin_port = htons(localport);
  38. result = bind(g_hServerSocket,(struct sockaddr *)&server_addr,sizeof(server_addr) );
  39. if( result != 0 )
  40. {
  41. printf("[tcp server] bind error!\n ");
  42. return -1;
  43. }
  44. // begin to listen
  45. result = listen(g_hServerSocket,5);
  46. if( result != 0 )
  47. {
  48. printf("[tcp server] listen error!\n ");
  49. return -1;
  50. }
  51. // 注: ServerEnv用于给服务线程传参,定义于serverIf.h中
  52. ServerEnv env;
  53. while(1)
  54. {
  55. client_len = sizeof(client_addr);
  56. clientSocket = accept(g_hServerSocket,(struct sockaddr *)&client_addr,&client_len );
  57. if( clientSocket < 0 )
  58. {
  59. printf("[tcp server] accept error!\n" );
  60. return -1;
  61. }
  62. env.m_hSocket = clientSocket;
  63. // add new tcp server thread
  64. add_new_tcp_process_thr(&env);
  65. }
  66. return 0;
  67. }
  68. int close_port()
  69. {
  70. printf("close server port, stop listen!\n");
  71. close(g_hServerSocket);
  72. return 0;
  73. }

四、服务器端服务线程代码

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    serverIf.c
  7. /// @brief   tcp服务线程代码
  8. ///
  9. /// 实现tcp服务线程相关接口
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #include "serverIf.h"
  20. #include "../include/Command.h"
  21. #include <sys/socket.h>
  22. #include <stdio.h>
  23. #include <string.h>
  24. #include <errno.h>
  25. int add_new_tcp_process_thr( ServerEnv *envp )
  26. {
  27. pthread_t tcpThr;
  28. if( pthread_create( &tcpThr,NULL,tcpServerThrFxn,envp ) )
  29. {
  30. printf("tcp thread create fail!\n");
  31. return -1;
  32. }
  33. printf("tcp thread has been created!\n");
  34. return 0;
  35. }
  36. int save_user_name( char * pUsername )
  37. {
  38. printf("ok,user name saved,username=%s\n",pUsername);
  39. return 0;
  40. }
  41. int save_user_age( int age )
  42. {
  43. printf("ok,user age saved,userage=%d\n",age);
  44. return 0;
  45. }
  46. void * tcpServerThrFxn( void * arg )
  47. {
  48. ServerEnv * envp = (ServerEnv *)arg;
  49. int socketfd = envp->m_hSocket;
  50. int returnBytes;
  51. ServerPack sPack;
  52. ReturnPack rPack;
  53. memcpy(rPack.cmdHeader,TCP_CMD_HEADER_STR,TCP_CMD_HEADER_LEN);
  54. while(1)
  55. {
  56. // read cmd from client
  57. returnBytes = recv(socketfd,(uint8_t *)&sPack,SERVER_PACK_LEN,0);
  58. if( returnBytes == SERVER_PACK_LEN )
  59. {
  60. //printf("ok,recv %d bytes! \n",SERVER_PACK_LEN);
  61. }
  62. else if( returnBytes <= 0 && errno != EINTR && errno != EWOULDBLOCK && errno != EAGAIN )
  63. {
  64. printf("disconnected or error occur! errno=%d\n ",errno);
  65. break;
  66. }
  67. else
  68. {
  69. continue;
  70. }
  71. // check the header
  72. if ( memcmp( sPack.cmdHeader, TCP_CMD_HEADER_STR, TCP_CMD_HEADER_LEN ) != 0 )
  73. {
  74. rPack.returnCMD = DVS_RETURN_INVLID_HEADER;
  75. // return error info to client
  76. returnBytes = send(socketfd,(uint8_t *)&rPack,RETURN_PACK_LEN,0 ) ;
  77. if( returnBytes < RETURN_PACK_LEN)
  78. {
  79. printf("send error!\n");
  80. continue;
  81. }
  82. }
  83. // analyse cmd
  84. rPack.returnCMD = DVS_RETURN_SUCCESS;
  85. switch( sPack.serverCMD )
  86. {
  87. case CMD_SAVE_USER_NAME:
  88. {
  89. if( save_user_name(sPack.Parameters.UserName.username) != 0)
  90. {
  91. rPack.returnCMD = DVS_RETURN_FAIL;
  92. }
  93. }
  94. break;
  95. case CMD_SAVE_USER_AGE:
  96. {
  97. if( save_user_age(sPack.Parameters.UserAge.userage) != 0)
  98. {
  99. rPack.returnCMD = DVS_RETURN_FAIL;
  100. }
  101. }
  102. break;
  103. default:
  104. {
  105. rPack.returnCMD = DVS_RETURN_INVLID_CMD;
  106. }
  107. break;
  108. }
  109. // return result info to client
  110. returnBytes = send(socketfd,(uint8_t *)&rPack,RETURN_PACK_LEN,0 );
  111. if( returnBytes < RETURN_PACK_LEN )
  112. {
  113. printf("send error!\n");
  114. continue;
  115. }
  116. }
  117. printf("close session socket!");
  118. // close socket
  119. close(socketfd);
  120. return (void*)0;
  121. }

五、客户端测试代码

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    main.c
  7. /// @brief   tcp客户端测试代码
  8. ///
  9. /// 实现 tcp客户端测试
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #include <stdio.h>
  20. #include "client.h"
  21. #define LOCAL_IP_STR "127.0.0.1"
  22. #define DEST_IP_STR  "192.201.0.8"
  23. #define DEST_PORT    8000
  24. int main(int argc, char **argv)
  25. {
  26. int i =0;
  27. printf("tcp test start!\n");
  28. if( connect_server(DEST_IP_STR,DEST_PORT) != 0)
  29. {
  30. return -1;
  31. }
  32. ServerPack sPack;
  33. sPack.serverCMD = CMD_SAVE_USER_AGE;
  34. sPack.Parameters.UserAge.userage = 20;
  35. if( send_cmd(sPack) == -1 )
  36. {
  37. printf("send cmd fail!\n");
  38. }
  39. getchar();
  40. getchar();
  41. close_connect();
  42. printf("tcp test pass!\n");
  43. return 0;
  44. }

六、服务器端测试代码

  1. //
  2. //  COPYRIGHT NOTICE
  3. //  Copyright (c) 2011, 华中科技大学 ticktick(版权声明)
  4. //  All rights reserved.
  5. //
  6. /// @file    main.c
  7. /// @brief   tcp客户端代码
  8. ///
  9. /// 实现tcp服务器端测试的代码
  10. ///
  11. /// @version 1.0
  12. /// @author  lujun
  13. /// @E-mail  lujun.hust@gmail.com
  14. /// @date    2011/08/21
  15. //
  16. //
  17. //  修订说明:
  18. //
  19. #include <stdio.h>
  20. #include "server.h"
  21. #include "serverIf.h"
  22. #define LOCAL_PORT 8000
  23. int main(int argc, char **argv)
  24. {
  25. printf("tcp test start!\n");
  26. if( open_port(LOCAL_PORT) != 0)
  27. {
  28. return -1;
  29. }
  30. close_port();
  31. printf(" close port !\n");
  32. return 0;
  33. }

七、总结和说明

本文后面的附件中有完整的代码,欢迎下载使用。编译方法,把代码文件夹都拷贝到linux下,在本代码文件夹的根目录下,运行make,即可生成对应的可执行文件。在运行测试程序的时候,请先执行server.out,然后执行client.out

另外,如果需要转载本文或者代码,请注明出处,本代码来自ticktick的博客:http://ticktick.blog.51cto.com 谢谢。

如果发现本代码的任何bug或者有任何建议,欢迎留言或者来信交流。

转载于:https://blog.51cto.com/ticktick/645523

基于Linux的socket编程模板相关推荐

  1. 基于Linux的socket网络编程项目——游侠手机商城

    基于Linux的socket网络编程项目--游侠手机商城 一.项目说明 二.项目使用的技术 三.客户端搭建 四.服务器端搭建 一.项目说明 本项目是一个仿真手机商城类系统,基本功能: 登录界面功能:用 ...

  2. Linux下Socket编程

    Linux下Socket编程    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...

  3. linux系统udp通信程序,Linux UDP socket编程(UDP通讯模型) | C/C++程序员之家

    Linux UDP socket编程(UDP通讯模型): UDPClient + UDPService. Linux下大多数网络程序都是基于TCP的,很少基于UDP,简单的通讯模型如下,开发时候备用! ...

  4. api有哪些 javasocket_基于java的socket编程及API解析

    一.socket通讯过程 1.socket与socket编程简介: socket 被翻译为"套接字",它是计算机之间进行通信的一种约定或一种方式.通过 socket 这种约定,一台 ...

  5. 基于Linux的网络编程——网络聊天程序

    网络聊天程序是目前应用极为广泛的一种网络软件,对于方便人们的交流沟通非常有效,同时,作为一种典型的网络应用,编写网络聊天程序是学习基于Linux的网络编程的有效方法. 结合任务需求设计该程序,程序采用 ...

  6. 基于Linux的Socket编程之TCP全双工Server-Client聊天程序

    转载:http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a ...

  7. Linux的SOCKET编程 简单演示

    转载:http://blog.csdn.net/hguisu/article/details/7445768/ Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源 ...

  8. asp.core api 通过socket和服务器通信发送udp_详解Linux的SOCKET编程

    文章来自于 https://www.zhangshengrong.com/p/9Oabd95XdK/ PHP进阶学习交流QQ群:983229225 本篇文章对Linux的SOCKET编程进行了详细解释 ...

  9. 一文了解linux下socket编程

    一文了解linux下socket编程 文章目录 一文了解linux下socket编程 1 网络编程的相关简述 1.1 引言 1.2 Tcp和Udp简介 1.3 TCP三次握手和四次挥手 1.4 网络编 ...

最新文章

  1. Dubbo基础专题——第一章(带你认识Dubbo)
  2. Matlab中pickic_法语「野餐」怎么写?不是picnic哦
  3. Velocity模板引擎的简单使用
  4. CSS 学习-文本 段落
  5. 面试精选:链表问题集锦
  6. CentOS 5.3 安装nginx+mysql+php
  7. 数组 边界 检查的几种实现方法
  8. SELECT命令中的GROUPBY和HAVING子句
  9. leetcode543. 二叉树的直径
  10. mipi和isp处理_图像信号处理 (ISP) 流水线
  11. bootstrap modal 一闪
  12. Python+pillow计算椭圆图形几何中心
  13. SEO能给独立站系统带来巨大的搜索流量吗?
  14. Mac局域网本地库server, CornerStone使用
  15. 转 linux shell 数组建立及使用技巧
  16. this version of the Java Runtime only recognizes class file versions up to 52.0
  17. 在Eclipse上用JAVA连接数据库
  18. 12.2 剪贴板的高级用法
  19. ionic emulate实时调试修改
  20. 为什么微信显示这个android设备,微信显示安卓手机型号在哪设置

热门文章

  1. 为什么浮点型运算结果会有误差?
  2. [转]LoadRunner 各个指标分析
  3. ThinkPHP 目录结构
  4. BZOJ 1003: [ZJOI2006]物流运输trans
  5. 兼容Mono的下一代云环境Web开发框架ASP.NET vNext
  6. php偷取,PHP偷取UTF-8目标网页内容输出为空白
  7. ios请求头解决参数中文乱码_解决请求参数的中文乱码问题(get、post)
  8. 神经网络的分类行为怎么就不能是一种力的行为?
  9. 由隐藏层节点数引起的网络准确率的不规则变化02
  10. 学习率对神经网络迭代次数的影响