当前位置: 首页 > news >正文

socket计划——一个简单的例子

从一个简单易用TCP样品开始socket计划,的基本过程例如下列:

server                                                  client

 

+++++++                                          ++++++++

创建socket                                          创建socket

+++++++                                          ++++++++

      |                                                         |

      |                                                         |

      |                                                         |

+++++++                                          ++++++++

地址赋值(                                           地址赋值(

自己的地址)                                        server地址)

+++++++                                          ++++++++

      |                                                         |

      |                                                         |

      |                                                         |

++++++++                                              |

用bind绑定                                                |

socket和地址                                             |

++++++++                                              |

      |                                                         |

      |                                                         |

      |                                                         |

+++++++                                                 |

listen                                                         |

+++++++                                                  |

      |                                                    ++++++++++

      |   <------------------------------          connect server         

      |                                                    ++++++++++

+++++++                                                  |

accept                                                        |

+++++++                                                  |

      |                                                           |

      |                                                    +++++++++

      |                                                     recv 和send

      |                                                     进行数据处理

      |                                                     +++++++++

+++++++++                                                |

用accept得到                                                 |

的socket进行                                                 |

recv 和 send                                                 |

+++++++++                                                |

      |                                                             |

      |                                                             |

      |                                                             |

+++++++++                                        +++++++++

close socket                                         close socket

+++++++++                                        +++++++++

 

依据以上步骤,server端的代码为

#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <stdlib.h> #include <syslog.h> #include <errno.h> #define MAX_LISTEN_NUM 5 #define SEND_BUF_SIZE 100 #define RECV_BUF_SIZE 100 #define LISTEN_PORT 1010 int main() { int listen_sock = 0; int app_sock = 0; struct sockaddr_in hostaddr; struct sockaddr_in clientaddr; int socklen = sizeof(clientaddr); char sendbuf[SEND_BUF_SIZE] = {0}; char recvbuf[RECV_BUF_SIZE] = {0}; int sendlen = 0; int recvlen = 0; int retlen = 0; int leftlen = 0; char *ptr = NULL; memset((void *)&hostaddr, 0, sizeof(hostaddr)); memset((void *)&clientaddr, 0, sizeof(clientaddr)); hostaddr.sin_family = AF_INET; hostaddr.sin_port = htons(LISTEN_PORT); hostaddr.sin_addr.s_addr = htonl(INADDR_ANY); listen_sock = socket(AF_INET, SOCK_STREAM, 0); if(listen_sock < 0) { syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__); exit(1); } if(bind(listen_sock, (struct sockaddr *)&hostaddr, sizeof(hostaddr)) < 0) { syslog(LOG_ERR, "%s:%d, bind socket failed", __FILE__, __LINE__); exit(1); } if(listen(listen_sock, MAX_LISTEN_NUM) < 0) { syslog(LOG_ERR, "%s:%d, listen failed", __FILE__, __LINE__); exit(1); } while(1) { app_sock = accept(listen_sock, (struct sockaddr *)&clientaddr, &socklen); if(app_sock < 0) { syslog(LOG_ERR, "%s:%d, accept failed", __FILE__, __LINE__); exit(1); } sprintf(sendbuf, "welcome %s:%d here!/n", inet_ntoa(clientaddr.sin_addr.s_addr), clientaddr.sin_port); //send data sendlen = strlen(sendbuf) +1; retlen = 0; leftlen = sendlen; ptr = sendbuf; //while(leftlen) { retlen = send(app_sock, ptr, sendlen, 0); if(retlen < 0) { if(errno == EINTR) retlen = 0; else exit(1); } leftlen -= retlen; ptr += retlen; } //receive data recvlen = 0; retlen = 0; ptr = recvbuf; leftlen = RECV_BUF_SIZE -1; //do { retlen = recv(app_sock, ptr, leftlen, 0) ; if(retlen < 0) { if(errno == EINTR) retlen = 0; else exit(1); } recvlen += retlen; leftlen -= retlen; ptr += retlen; } //while(recvlen && leftlen); printf("receive data is : %s", recvbuf); close(app_sock); } close(listen_sock); return 0; }

 

 

client代码为:

#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <syslog.h> #include <errno.h> #include <stdlib.h> #define MAX_LISTEN_NUM 5 #define SEND_BUF_SIZE 100 #define RECV_BUF_SIZE 100 #define SERVER_PORT 1010 int main() { int sock_fd = 0; char recvbuf[RECV_BUF_SIZE] = {0}; char sendbuf[SEND_BUF_SIZE] = {0}; int recvlen = 0; int retlen = 0; int sendlen = 0; int leftlen = 0; char *ptr = NULL; struct sockaddr_in ser_addr; memset(&ser_addr, 0, sizeof(ser_addr)); ser_addr.sin_family = AF_INET; inet_aton("127.0.0.1", (struct in_addr *)&ser_addr.sin_addr); ser_addr.sin_port = htons(SERVER_PORT); sock_fd = socket(AF_INET, SOCK_STREAM, 0); if(sock_fd < 0) { syslog(LOG_ERR, "%s:%d, create socket failed", __FILE__, __LINE__); exit(1); } if(connect(sock_fd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) < 0) { syslog(LOG_ERR, "%s:%d, connect socket failed", __FILE__, __LINE__); exit(1); } //receive data recvlen = 0; retlen = 0; ptr = recvbuf; leftlen = RECV_BUF_SIZE -1; //do { retlen = recv(sock_fd, ptr, leftlen, 0) ; if(retlen < 0) { if(errno == EINTR) retlen = 0; else exit(1); } recvlen += retlen; leftlen -= retlen; ptr += retlen; } //while(recvlen && leftlen); printf("receive data is : %s", recvbuf); sprintf(sendbuf, "hello server/n"); //send data sendlen = strlen(sendbuf) +1; retlen = 0; leftlen = sendlen; ptr = sendbuf; // while(leftlen) { retlen = send(sock_fd, ptr, sendlen, 0); if(retlen < 0) { if(errno == EINTR) retlen = 0; else exit(1); } leftlen -= retlen; ptr += retlen; } close(sock_fd); }

 

 

如今一个简单的使用tcp的socket通信的样例已经完毕了。这里有几个须要说明的问题

1)头文件:

sys/socket.h   包括了socket相关的函数,如socket。send 和recv, 以及struct sockaddr等

netinet/in.h    包括了地址结构。如struct sockaddr_in

errno.h           包括了errno 和 EINTR

syslog.h         包括了syslog相关的信息,其打印结果在/var/log/messages里面

 

2)socket地址

对于IPv4来说,其地址用的是struct sockaddr_in,详细结构例如以下

struct in_addr {
  in_addr_t   s_addr;           /* 32-bit IPv4 address */
                                /* network byte ordered */
};

struct sockaddr_in {
  uint8_t         sin_len;      /* length of structure (16) */
  sa_family_t     sin_family;   /* AF_INET */
  in_port_t       sin_port;     /* 16-bit TCP or UDP port number */
                                /* network byte ordered */
  struct in_addr  sin_addr;     /* 32-bit IPv4 address */
                                /* network byte ordered */
  char            sin_zero[8];  /* unused */
};


当中sin_len我们一般不关注。也不填(仅仅有在使用routing socket的时候才用到,被内核用来处理各种协议簇的地址结构)。
bind, connect, sendto, 和 sendmsg会把socket地址从程序传递给内核; 而accept, recvfrom, recvmsg, getpeername, 和 getsockname会把地址从内核传递给程序。由于不同协议簇的地址结构是不一样的。所以必需要有一个通用的指针来传递地址。对于ANSI C来说我们一般使用void *,可是socket产生早于ANSI C。所以也就没有使用这个机制,而是使用一个通用的地址结构struct sockaddr来处理的
struct sockaddr { uint8_t sa_len; sa_family_t sa_family; /* address family: AF_xxx value */ char sa_data[14]; /* protocol-specific address */ };

IPv6的socket地址为struct sockaddr_in6

struct in6_addr { uint8_t s6_addr[16]; /* 128-bit IPv6 address */ /* network byte ordered */ }; #define SIN6_LEN /* required for compile-time tests */ struct sockaddr_in6 { uint8_t sin6_len; /* length of this struct (28) */ sa_family_t sin6_family; /* AF_INET6 */ in_port_t sin6_port; /* transport layer port# */ /* network byte ordered */ uint32_t sin6_flowinfo; /* flow information, undefined */ struct in6_addr sin6_addr; /* IPv6 address */ /* network byte ordered */ uint32_t sin6_scope_id; /* set of interfaces for a scope */ };

对于sockaddr-in6来说。我们不能用通用的地址struct sockaddr来存储了,而是产用新的通用地址结构struct sockaddr_storage,这个结构足够大能够存储不论什么系统支持的地址。
struct sockaddr_storage { uint8_t ss_len; /* length of this struct (implementation dependent) */ sa_family_t ss_family; /* address family: AF_xxx value */ /* implementation-dependent elements to provide: * a) alignment sufficient to fulfill the alignment requirements of * all socket address types that the system support * b) enough storage to hold any type of socket address that the * system supports. */ };
几种常见的地址结构

3) 相关函数的的length
对于从程序传地址给内核的函数(如connect),其长度是一个整型值。告诉内核要copy的地址长度。


对于从内核传递给程序的函数(如accpt)。其长度是一个整型指针。是一个value-result參数。

有两个目的:一告诉内核地址结构的长度。让内核在copy的时候不要超过这个长度;二返回内核真正copy的长度。

4)字节序
socket相关的函数都是使用网络字节序

5)地址转换函数
inet_aton, inet_ntoa, and inet_addr把IPv4字符串地址转为32位的网络字节序地址
inet_ptonand inet_ntop能够转换IPv4和IPv6的地址


6)listen中的backlog
要知道这个值的含义先用说一下。对于一个listen的socket,有两个队列:一个是incomplete connection队列(只收到SYN)。一个是complete connection队列(三次握手完毕)。accept函数就是在complete connection队列中取一个socket。

backlog就是指队列的个数。但不行的是各个地方都没有明白定义这个值,没有说明到底代表了哪个队列,或是两个队列之和。一般来说能够
同一时候处理的连接数是backlog的1.5倍,非常多地方都用5.

7) getsockname 和 getpeername
这两个函数能够与socket关联的地址,getsockname 和 getpeername分别得到自己和对端的地址

int getsockname(int sockfd, struct sockaddr *localaddr, socklen_t *addrlen)

int getpeername(int sockfd , struct sockaddr * peeraddr , socklen_t * addrlen ); 



















 

 

 

 

 

转载于:https://www.cnblogs.com/mengfanrong/p/4570809.html

相关文章:

  • java同学毕业后学习之路建议
  • 【必备】史上最全的浏览器 CSS JS Hack 手册(转)
  • ElementUI Tree树形控件renderContent return时报错
  • iOS Widget简单使用
  • FaceBook页面加载技术
  • 缴款日后流动性仍紧 央票利率再现“倒挂”
  • HIDKomponente使用读写Hid设备(转)
  • setPreferredSize和setSize的区别及用法
  • 人们为何喜欢在社交网络发布个人状态?
  • iOS常用动画
  • 信息增益的特征选择方法
  • 关于自定义强类型实体类的一点困惑
  • 记录学习MVC过程,MVC验证(四)
  • nginx 转发 header 数据丢失
  • unity5,UI Button too small on device than in Game View解决办法
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • 230. Kth Smallest Element in a BST
  • Codepen 每日精选(2018-3-25)
  • docker python 配置
  • Java 内存分配及垃圾回收机制初探
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Java程序员幽默爆笑锦集
  • python学习笔记-类对象的信息
  • React-Native - 收藏集 - 掘金
  • redis学习笔记(三):列表、集合、有序集合
  • springboot_database项目介绍
  • text-decoration与color属性
  • vue的全局变量和全局拦截请求器
  • 二维平面内的碰撞检测【一】
  • 两列自适应布局方案整理
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 码农张的Bug人生 - 见面之礼
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 双管齐下,VMware的容器新战略
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 移动端解决方案学习记录
  • Hibernate主键生成策略及选择
  • 如何用纯 CSS 创作一个货车 loader
  • ​软考-高级-系统架构设计师教程(清华第2版)【第15章 面向服务架构设计理论与实践(P527~554)-思维导图】​
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #pragma data_seg 共享数据区(转)
  • (8)STL算法之替换
  • (BFS)hdoj2377-Bus Pass
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (day 12)JavaScript学习笔记(数组3)
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (Pytorch框架)神经网络输出维度调试,做出我们自己的网络来!!(详细教程~)
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (动态规划)5. 最长回文子串 java解决
  • (附源码)php新闻发布平台 毕业设计 141646
  • (附源码)springboot社区居家养老互助服务管理平台 毕业设计 062027
  • (一)Neo4j下载安装以及初次使用
  • (原創) 如何刪除Windows Live Writer留在本機的文章? (Web) (Windows Live Writer)
  • (转)AS3正则:元子符,元序列,标志,数量表达符