Linux Socket 摘要(二)(基于TCP的C/S基本实现,相关基础知识,非阻塞select)

本文主要是介绍Linux Socket 摘要(二)(基于TCP的C/S基本实现,相关基础知识,非阻塞select),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

#PS:要转载请注明出处,本人版权所有

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷

测试环境:

Linux 4.10.0-33-generic #37~16.04.1-Ubuntu SMP Fri Aug 11 14:07:24 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

1 关于linux socket通信,要详细了解清楚,不知道要说多少天。所以网上大部分教程也是只介绍了基本的api调用流程。一些其他的问题还没有提及,当然本文作者由于水平有限,估计也只能介绍个流程,并且解决一些简单的未涉及的问题。

2 TCP的基本要点,三次握手,四次分手,分别代表了开始和结束。下图是我在百度图片上找的一个图,完全找不到原图出自哪里,很伤感。
这里写图片描述
说明:此图完全清晰可见的描述了一个tcp通信到底做了一些什么。我也不详细说明,改天可以给大家抓包分析分析。

3 通过上图我们可以看到,客户端connect后,就可以write和read了,而服务端accept后可以做同样的事情,最后只需要close就能够解决。下面我们简要的来分析一下这个流程。
这里写图片描述
这里写图片描述
上图是我写的一个服务端程序跑起来后,通过netstat可以看到此进程进入了listen状态。
这里写图片描述
这里写图片描述
这里写图片描述
上面三个图演示了一个tcp通信的完整过程。第三图1-3是connect,4是write:Hello Server,5是对4的响应,代表收到,6是write:Hello Client,7同理5,8-11对应close,和上文所要展示的流程基本相同。
这里写图片描述
上图是查看端口,可见tcp的链接状况(图中pid和上文图中pid不对应的原因是非同一个测试)。

4 好了,上文BB了那么多,只是要科普一下而已,现在进入正题,首先来看几个定义及结构体。
结构体1

    typedef unsigned short __kernel_sa_family_t;typedef __kernel_sa_family_t    sa_family_t;      struct sockaddr {//通用结构体,很多socket相关api都要使用它sa_family_t sa_family;char        sa_data[14];}

结构体2

    typedef uint32_t in_addr_t;struct in_addr//ip地址存放结构体{in_addr_t s_addr;};#define __SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##familystruct sockaddr_in//ip4 地址结构{__SOCKADDR_COMMON (sin_);in_port_t sin_port;                 //Port number.  struct in_addr sin_addr;            // Internet address.  // Pad to size of `struct sockaddr'unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};

结构体3

    #define _K_SS_MAXSIZE   128      //Implementation specific max size #define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *))//Implementation specific desired alignment typedef unsigned short __kernel_sa_family_t;struct __kernel_sockaddr_storage {//此结构体是新内核提出的,可以存储所有协议地址类型__kernel_sa_family_t    ss_family;              //address family // Following field(s) are implementation specific char            __data[_K_SS_MAXSIZE - sizeof(unsigned short)];// space to achieve desired size, // _SS_MAXSIZE value minus size of ss_family } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));   /* force desired alignment 

结构体1,2是ip4网络编程常用的一些结构体,结构体3是新内核对于多种协议地址结构提出的一个新的通用的存储结构体。

5 关于这些结构体的知识我们就到这里了。现在来讲一讲linux上的socket编程。下面就是cs通信中,各自要使用的api及调用顺序,这也是最基本的socket通信,也是网上流传最广的通信例子。

client:
int socket(int domain, int type, int protocol);
int connect(int sockfd, const struct sockaddr *addr,  socklen_t addrlen);
read/write
close
server:
int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//阻塞io
read/write
close

6 非阻塞通信,关键select(注意,这里没有使用更高级的epoll,因为我对这个api也是一个菜鸡)

       int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);

对于这个api,简单来说,就是监控 所有传入的 文件描述符集合,当相关文件描述集合可读, 可写 ,异常时,select正常返回,否则可能是等待超时,可能是出错。
对于本文,就是监控客户端socket fd,监控服务端socket fd和accept接受的fd。

7 其他的都不说了,口水都干了,直接上例子代码,然后喝口水,上个厕所,洗个手,坐等下班
client.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>#include <errno.h>
#include <string.h>#include <arpa/inet.h>#include <sys/select.h>/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>#define TARGETPORT 6666
#define TARGETIP "127.0.0.1"
int main(int argc, char *argv[])
{/*
int socket(int domain, int type, int protocol);domain:AF_UNIX, AF_LOCAL   Local communication              unix(7)AF_INET             IPv4 Internet protocols          ip(7)AF_INET6            IPv6 Internet protocols          ipv6(7)AF_IPX              IPX - Novell protocolsAF_NETLINK          Kernel user interface device     netlink(7)AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)AF_AX25             Amateur radio AX.25 protocolAF_ATMPVC           Access to raw ATM PVCsAF_APPLETALK        AppleTalk                        ddp(7)AF_PACKET           Low level packet interface       packet(7)AF_ALG              Interface to kernel crypto API
type:SOCK_STREAM     Provides sequenced, reliable, two-way, connection-basedbyte  streams.  An out-of-band data transmission mecha‐nism may be supported.SOCK_DGRAM      Supports datagrams (connectionless, unreliable messagesof a fixed maximum length).SOCK_SEQPACKET  Provides  a  sequenced,  reliable,  two-way connection-based data transmission path  for  datagrams  of  fixedmaximum  length;  a  consumer  is  required  to read anentire packet with each input system call.SOCK_RAW        Provides raw network protocol access.SOCK_RDM        Provides a reliable datagram layer that does not  guar‐antee ordering.SOCK_PACKET     Obsolete  and  should  not be used in new programs; seepacket(7).protocol:The protocol specifies a  particular  protocol  to  be  used  with  thesocket.  Normally only a single protocol exists to support a particularsocket type within a given protocol family, in which case protocol  canbe  specified  as  0.   However, it is possible that many protocols mayexist, in which case a particular protocol must be  specified  in  thismanner.   The  protocol number to use is specific to the “communicationdomain” in which communication is to take place; see protocols(5).  Seegetprotoent(3) on how to map protocol name strings to protocol numbers.
*/int fd;if (0 > (fd = socket(AF_INET, SOCK_STREAM, 0))){perror("socket create error:");return -1;}/*int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);typedef unsigned short __kernel_sa_family_t;typedef __kernel_sa_family_t    sa_family_t;      struct sockaddr {sa_family_t sa_family;char        sa_data[14];}typedef uint32_t in_addr_t;struct in_addr{in_addr_t s_addr;};#define __SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##familystruct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port;                 /* Port number.  struct in_addr sin_addr;            /* Internet address.  // Pad to size of `struct sockaddr'.  unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};#define _K_SS_MAXSIZE   128      //Implementation specific max size #define _K_SS_ALIGNSIZE (__alignof__ (struct sockaddr *))//Implementation specific desired alignment typedef unsigned short __kernel_sa_family_t;struct __kernel_sockaddr_storage {__kernel_sa_family_t    ss_family;              /* address family /* Following field(s) are implementation specific char            __data[_K_SS_MAXSIZE - sizeof(unsigned short)];/* space to achieve desired size, *//* _SS_MAXSIZE value minus size of ss_family } __attribute__ ((aligned(_K_SS_ALIGNSIZE)));   /* force desired alignment */struct sockaddr_in addr;memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_port = htons(TARGETPORT);//addr.sin_addr.s_addr = htonl//int inet_pton(int af, const char *src, void *dst);inet_pton(AF_INET, TARGETIP, (void *)&(addr.sin_addr.s_addr));//int connect(int sockfd, const struct sockaddr *addr,\socklen_t addrlen);if (0 > connect(fd, (const struct sockaddr *)&addr, sizeof(struct sockaddr))){perror("socket connect error:");return -1;}int ret;struct timeval timeout;char  SendMsg[] = {"Hello Server"};char RecBuf[100] = {0};fd_set rec_set;FD_ZERO(&rec_set);  FD_SET(fd, &rec_set);  while (1){//ssize_t write(int fd, const void *buf, size_t count);if ( 0 > write(fd, SendMsg, sizeof(SendMsg)) ){printf("write failed\n");}timeout.tv_sec = 5;   timeout.tv_usec = 0;  //int select(int nfds, fd_set *readfds, fd_set *writefds,\fd_set *exceptfds, struct timeval *timeout);ret = select(fd + 1, &rec_set, NULL, NULL, &timeout); //select返回表示检测到可读事件 switch(ret){case 0:{printf("select timeout!\n");break;}case -1:{perror("select error:");return -1;break;}default:{//ssize_t read(int fd, void *buf, size_t count);read(fd, RecBuf, sizeof(SendMsg));printf("RecBuf:%s\n",RecBuf);sleep(1);}}}close(fd);return 0;
}

server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>#include <errno.h>
#include <string.h>#include <arpa/inet.h>#include <sys/select.h>/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>#include <sys/select.h>#define TARGETPORT 6666
#define TARGETIP "127.0.0.1"
typedef struct _MyFdSet
{int FdNum;int AllFdSet[FD_SETSIZE];
} MyFdSet;void MyFdSet_INSERT(MyFdSet *set, int fd){set->AllFdSet[set->FdNum] = fd; set->FdNum++;    
}int  MyFdSet_GETMAX(MyFdSet *set){int i = 1;int max_fd = set->AllFdSet[0];for ( ; i < set->FdNum; i ++) { if ( max_fd < set->AllFdSet[i]){max_fd = set->AllFdSet[i];}}return max_fd;
}void MyFdSet_REMOVE(MyFdSet * set,int fd) {MyFdSet tmp;tmp.FdNum = 0;int i = 0;for ( ; i < set->FdNum; i++ ){if ( fd != set->AllFdSet[i] ){tmp.AllFdSet[tmp.FdNum] = set->AllFdSet[i];tmp.FdNum++;}}set->FdNum = tmp.FdNum;for ( i = 0; i < set->FdNum; i++){set->AllFdSet[i] = tmp.AllFdSet[i];}
}int main(int argc, char *argv[]){int fd;if (0 > (fd = socket(AF_INET, SOCK_STREAM, 0))){perror("socket error:");return -1;}struct sockaddr_in addr;struct sockaddr_in client_addr;memset(&addr, 0, sizeof(struct sockaddr_in));addr.sin_family = AF_INET;addr.sin_port = htons(TARGETPORT);addr.sin_addr.s_addr = htonl(INADDR_ANY);//   int bind(int sockfd, const struct sockaddr *addr,\socklen_t addrlen);if (0 > bind(fd, (const struct sockaddr *)&addr, sizeof(struct sockaddr))){perror("bind error:");return -1;}//int listen(int sockfd, int backlog);if (0 > listen(fd, 5)){perror("bind error:");return -1;}fd_set ser_set;FD_ZERO(&ser_set);FD_SET(fd, &ser_set);struct timeval timeout;int ret;MyFdSet myfdset = {0,{0}};MyFdSet_INSERT(&myfdset, fd);char RecBuf[100];char SendMsg[] = {"Hello Client"}; while (1){int n = 0;FD_ZERO(&ser_set);for ( ; n < myfdset.FdNum; n++){FD_SET(myfdset.AllFdSet[n], &ser_set);printf("exist fd %d\n",myfdset.AllFdSet[n]);}timeout.tv_sec = 5;timeout.tv_usec = 0;//int select(int nfds, fd_set *readfds, fd_set *writefds,\fd_set *exceptfds, struct timeval *timeout);ret = select( MyFdSet_GETMAX(&myfdset) + 1, &ser_set, NULL, NULL, &timeout);switch (ret){case 0:{printf("select timeout!\n");break;}case -1:{perror("select error:");return -1;break;}default:{  int i = 0;for ( ; i < myfdset.FdNum; i ++ ){if (FD_ISSET(myfdset.AllFdSet[i], &ser_set)){if ( fd == myfdset.AllFdSet[i] ){//client connectint c_fd;int client_addr_len = sizeof(struct sockaddr);if ( 0  >  (c_fd = accept(fd, (struct sockaddr*)&client_addr, &client_addr_len)) ){perror("accept error:");break;}printf("client ip:%s,port:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));MyFdSet_INSERT(&myfdset, c_fd);FD_SET(c_fd, &ser_set);break;}else{//client sent datas in bufret = read(myfdset.AllFdSet[i], RecBuf, sizeof(SendMsg));if ( 0 > ret ){MyFdSet_REMOVE(&myfdset, myfdset.AllFdSet[i]);close(myfdset.AllFdSet[i]);perror("read error:");break;}else if( ret == 0 ){//client disconnectedMyFdSet_REMOVE(&myfdset, myfdset.AllFdSet[i]);close(myfdset.AllFdSet[i]);printf("client disconnected\n");break;}else{printf("RecMsg:%s\n", RecBuf);write(myfdset.AllFdSet[i], SendMsg, sizeof(SendMsg));}}}}sleep(1);}}}int i;for ( i = 0; i < myfdset.FdNum; i++ ){//close fdclose(myfdset.AllFdSet[i]);}return 0;}

直接gcc client.c -o client gcc server.c -o server 就可以使用了

#PS:请尊重原创,不喜勿喷

#PS:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复

这篇关于Linux Socket 摘要(二)(基于TCP的C/S基本实现,相关基础知识,非阻塞select)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065779

相关文章

linux生产者,消费者问题

pthread_cond_wait() :用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 pthread_cond_wait() 必须与pthread_mutex 配套使用。pthread_cond_wait()函数一进入wait状态就会自动release mutex。当其他线程通过pthread

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

Linux 安装、配置Tomcat 的HTTPS

Linux 安装 、配置Tomcat的HTTPS 安装Tomcat 这里选择的是 tomcat 10.X ,需要Java 11及更高版本 Binary Distributions ->Core->选择 tar.gz包 下载、上传到内网服务器 /opt 目录tar -xzf 解压将解压的根目录改名为 tomat-10 并移动到 /opt 下, 形成个人习惯的路径 /opt/tomcat-10

RecastNavigation之Poly相关类

Poly分成正常的Poly 和 OffMeshPoly。 正常的Poly 又分成 原始的Poly 和 Detail化的Poly,本文介绍这两种。 Poly的边分成三种类型: 1. 正常边:有tile内部的poly与之相邻 2.border边:没有poly与之相邻 3.Portal边:与之相邻的是外部tile的poly   由firstLink索引 得到第一个连接的Poly  通

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

【Linux进阶】UNIX体系结构分解——操作系统,内核,shell

1.什么是操作系统? 从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核(kerel),因为它相对较小,而且位于环境的核心。  从广义上说,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并使计算机具有自己的特生。这里所说的其他软件包括系统实用程序(system utility)、应用程序、shell以及公用函数库等

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘