Linux Socket 摘要(二)(基于TCP的C/S基本实现,相关基础知识,非阻塞select)
PS:要转载请注明出处,本人版权所有。
PS: 这个只是基于《我自己》的理解,
如果和你的原则及想法相冲突,请谅解,勿喷。
前置说明
本文作为本人csdn blog的主站的备份。(BlogID=039)
本文发布于 2017-08-31 16:18:21,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=039)
环境说明
测试环境: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
前言
无
Socket摘要2
关于linux socket通信,要详细了解清楚,不知道要说多少天。所以网上大部分教程也是只介绍了基本的api调用流程。一些其他的问题还没有提及,当然本文作者由于水平有限,估计也只能介绍个流程,并且解决一些简单的未涉及的问题。
TCP的基本要点,三次握手,四次分手,分别代表了开始和结束。下图是我在百度图片上找的一个图,完全找不到原图出自哪里,很伤感。
说明:此图完全清晰可见的描述了一个tcp通信到底做了一些什么。我也不详细说明,改天可以给大家抓包分析分析。
- 通过上图我们可以看到,客户端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不对应的原因是非同一个测试)。
- 好了,上文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##family
struct 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是新内核对于多种协议地址结构提出的一个新的通用的存储结构体。
- 关于这些结构体的知识我们就到这里了。现在来讲一讲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
- 非阻塞通信,关键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。
- 其他的都不说了,口水都干了,直接上例子代码,然后喝口水,上个厕所,洗个手,坐等下班
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 protocols
AF_NETLINK Kernel user interface device netlink(7)
AF_X25 ITU-T X.25 / ISO-8208 protocol x25(7)
AF_AX25 Amateur radio AX.25 protocol
AF_ATMPVC Access to raw ATM PVCs
AF_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-based
byte streams. An out-of-band data transmission mecha‐
nism may be supported.
SOCK_DGRAM Supports datagrams (connectionless, unreliable messages
of a fixed maximum length).
SOCK_SEQPACKET Provides a sequenced, reliable, two-way connection-
based data transmission path for datagrams of fixed
maximum length; a consumer is required to read an
entire 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; see
packet(7).
protocol:
The protocol specifies a particular protocol to be used with the
socket. Normally only a single protocol exists to support a particular
socket type within a given protocol family, in which case protocol can
be specified as 0. However, it is possible that many protocols may
exist, in which case a particular protocol must be specified in this
manner. The protocol number to use is specific to the “communication
domain” in which communication is to take place; see protocols(5). See
getprotoent(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##family
struct 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 connect
int 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 buf
ret = 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 disconnected
MyFdSet_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 fd
close(myfdset.AllFdSet[i]);
}
return 0;
}
直接gcc client.c -o client gcc server.c -o server 就可以使用了
后记
无
参考文献
- 无
打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
PS: 请尊重原创,不喜勿喷。
PS: 要转载请注明出处,本人版权所有。
PS: 有问题请留言,看到后我会第一时间回复。
Linux Socket 摘要(二)(基于TCP的C/S基本实现,相关基础知识,非阻塞select)的更多相关文章
- 【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念
linux设备驱动归纳总结(一):内核的相关基础概念 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- (转载)小白的linux设备驱动归纳总结(一):内核的相关基础概念---学习总结
1. 学习总结 小白的博客讲的linux内核驱动这一块的东西比较基础,因此想通过学习他的博客,搭配着看书的方式来学习linux内核和驱动.我会依次更新在学习小白的博客的过程的感悟和体会. 2.1 内核 ...
- iOS蓝牙开发(二)蓝牙相关基础知识
原文链接: http://liuyanwei.jumppo.com/2015/07/17/ios-BLE-1.html iOS蓝牙开发(一)蓝牙相关基础知识: 蓝牙常见名称和缩写 MFI ====== ...
- 网络编程----socket介绍、基于tcp协议的套接字实现、基于udp协议的套接字实现
一.客户端/服务器架构(C/S架构) 即C/S架构,包括: 1.硬件C/S架构(打印机) 2.软件C/S架 ...
- Linux网络编程:基于TCP的程序开发回顾篇《转》
面向连接的TCP程序设计 基于TCP的程序开发分为服务器端和客户端两部分,常见的核心步骤和流程: 其实按照上面这个流程调用系统API确实可以完全实现应用层程序的开发,一点问题没有.可随着时间的推移,你 ...
- 初识Socket通信:基于TCP和UDP协议学习网络编程
学习笔记: 1.基于TCP协议的Socket网络编程: (1)Socket类构造方法:在客户端和服务器端建立连接 Socket s = new Socket(hostName,port);以主机名和端 ...
- 什么是 socket?简述基于 tcp 协议的套接字通信流程?
Socket的英文原义是"孔"或"插座".通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄, 可以用来实现不同虚拟机或不同计 ...
- 读书笔记——网络编程与开发技术(3)基于TCP/IP协议的网络编程相关知识
TCP/IP协议:数据链路层,网络层,传输层,应用层. IP地址分为5类:A类.B类.C类.D类.E类. (A类.B类.C类是基本类,D类多用于多播传送,E类为保留类.) "*"表 ...
- TCP与UDP比较 以及并发编程基础知识
一.tcp比udp真正可靠地原因 1.为什么tcp比udp传输可靠地原因: 我们知道在传输数据的时候,数据是先存在操作系统的缓存中,然后发送给客户端,在客户端也是要经过客户端的操作系统的,因为这个过程 ...
- TCP/IP协议(一)网络基础知识
参考书籍为<图解tcp/ip>-第五版.这篇随笔,主要内容还是TCP/IP所必备的基础知识,包括计算机与网络发展的历史及标准化过程(简述).OSI参考模型.网络概念的本质.网络构建的设备等 ...
随机推荐
- 小知识:MAC上使用预览功能来减小PDF大小
工作中有些流程会用到PDF电子扫描件,当身边没有扫描设备时,通常会用手机拍照然后合成PDF. 有一个问题是:合成的PDF文件很大,甚至远大于照片本身大小.比如照片是4M的,合成的PDF文件就基本要30 ...
- 在Windows上使用.NET部署到Docker 《让孩子们走出大坑》
折腾Docker 有几天了,整别的都没这个糟心.目前已经顺利部署运行起来了.顺便给大家分享下处理在Windows上使用.NET部署到Docker的相关问题解决方法. 1. Docker无法安装问题 ...
- Argocd学习
argocd官网文档链接 ArgoCD官网文档 在K8S集群使用argocd命令将集群添加到argcd的cluster列表中 argocd cluster add kubernetes-admin@i ...
- 多层PCB线路板制作流程
PCB制作第一步是整理并检查pcb多层线路板布局(Layout).电路板制作工厂收到PCB设计公司的CAD文件,由于每个CAD软件都有自己独特的文件格式,所以深圳PCB板厂会转化为一个统一的格式Ger ...
- Laravel入坑指南(3)——模板
各位小伙伴有缘聚到这里,说明对于Laravel的路由和控制器已经有点了解了. 会写业务逻辑之后,如何把结果漂亮地展示出来,就是我们要解决的问题.(前后端分离的同学,请自动忽略)在MVC的世界里,漂亮的 ...
- nginx配置反向代理缓存
说明 最近运维一个网站里面含有不经常变化的小图片,而每次请求都需要调用file接口获取不太合适.所以就想利用nginx的反向代理缓存来减轻服务接口的请求压力. 工作原理 Nginx反向代理缓存,当客户 ...
- spring boot读取json文件并实现接口查询
0.源码下载 https://download.csdn.net/download/IndexMan/84238085 1.说明 最近需要在spring boot项目启动时读取json文件并保存到Li ...
- zookeeper源码(09)follower处理客户端请求
在zookeeper中,follower也可以接收客户端连接,处理客户端请求,本文将分析follower处理客户端请求的流程: 读请求处理 写请求转发与响应 follower接收转发客户端请求 网络层 ...
- 麒麟系统开发笔记(十一):在国产麒麟系统上使用gdb定位崩溃异常方法流程进阶定位代码行数及专项测试Demo
前言 上一篇,通过研究,可以定位到函数,本篇进一步优化,没有行数,程序较为复杂的时候,就无法定位,所以进一步定位. 本篇做了qBreakpad的研究,但是没有成功,过程也还是填出来,后来突然注意 ...
- java.lang.IllegalStateException: Failed to check the status of the service 的解决办法
参考资料 java.lang.IllegalStateException: Failed to check the status of the service 的解决办法_Hello_World_QW ...