linux c socket编程学习(1)(一些基本概念及基本的函数使用)
1、相关理论:
部分理论资料来自TCP/IP sockets编程(C语言实现)
,额外做了许多补充。
概念:
IP
:
每台主机的IP
地址都是不同的。主机的IP地址
有公网IP
和内网IP
。在局域网中,每台主机的IP
都取自192.168.1.1-192.168.1.255
(C类
)之间,不存在两台主机的IP
重复,此IP
是局域网的内网IP
,由用户自行分配。公网IP
是访问互联网的IP
,每次联机到互联网时会随机分配,互联网上的每一台主机的公网IP
都不重复。内网IP
通过交换机或路由器搭配主机构建,而公网IP
是由运营商运营,作用在ADSL设备
上。查看内网IP
时,通过主机终端执行命令ifconfig(Linux)
\ ipconfig(Windows)
查看,上面显示的IP
就是主机在当前路由的局域网中的IP
。而公网IP
则可以通过百度搜索IP地址
获取,如我当前使用的主机的公网IP
和内网IP
:
协议
:
是关于由通信程序交换的分组及其含义的协定。TCP/IP
协议族中的主要协议是IP(网络协议)
、TCP(传输控制协议)
和UDP(用户数据报协议)
,两者的共同功能:寻址
。
TCP:
TCP(传输控制协议)
是一种面向连接的、可靠的、基于字节流的传输层通信协议。
数据帧格式
:
(1)序号:Seq
序号,占32
位,用来标识从TCP
源端向目的端发送的字节流,发起方发送数据时对此进行标记。
(2)确认序号:Ack
序号,占32
位,只有ACK标志位为1
时,确认序号字段才有效,Ack=Seq+1
。
(3)标志位:共6
个,即URG
、ACK
、PSH
、RST
、SYN
、FIN
等,具体含义如下:
(A)URG
:紧急指针(urgent pointer
)有效。
(B)ACK
:确认序号有效。
(C)PSH
:接收方应该尽快将这个报文交给应用层。
(D)RST
:重置连接。
(E)SYN
:发起一个新连接。
(F)FIN
:释放一个连接。
来自:https://blog.csdn.net/sssnmnmjmf/article/details/68486261
UDP:
UDP(用户数据报协议)
是一种面向事务、简单不可靠信息传送、基于数据报的传输层通信协议。
填充字节表示如果数据长度为奇数,数据就长度加1。
局域网中数据流动图:
数据流向
:从应用程序开始,通过TCP
和IP
的实现,通过网络,并在另一端通过IP
和TCP
的实现进行备份。
TCP
和UDP
使用端口号来确定主机内的应用程序。TCP
和UDP
被称为端到端的传输协议,因为它们自始至终把数据从一个程序运送到另一个程序(而IP只把数据从一台主机运送到另一台主机)
。
TCP的可靠传输
:
TCP
被设计成在IP
提供的主机到主机信道中检测可能发生的丢失
、复制分组
及其他错误,并从中恢复过来。TCP
提供了可靠的字节流信道
,因此应用程序不需要处理这些问题。它是面向连接的协议
:在把它用于信道之前,两个程序必须先建立一条TCP连接
,这涉及在两台通信的计算机上的TCP
实现之间完成握手消息
的交换。使用TCP
在许多方面也类似于文件输入
\ 输出
(I/O
)。
TCP确保可靠传输的机制
:
(1)校验和
(2)序列号
(3)确认应答
(4)超时重传
(5)连接管理
(6)流量控制
(7)拥塞控制
其实都是计算机网络课教过的东西,如果忘记了,具体原理和过程可以参考:https://blog.csdn.net/liuchenxia8/article/details/80428157
TCP的三次握手建立连接和四次挥手断开连接原理可以参考:https://www.cnblogs.com/Andya/p/13291686.html
UDP的不可靠传输
:
与TCP
不同,UDP
不会尝试从IP
经历的错误中恢复,它只是扩展IP“尽力而为”
的数据报服务,使得它在应用程序之间(而不是主机之间)工作。因此,使用UDP
的应用程序必须准备好处理分组
、重新排序
等问题的方法和预备程序。
客户与服务器
:
客户程序发起通信,而服务器程序则被动地等待,然后响应联系它的客户端。客户端与服务器一起组成了应用程序。客户与服务器描述了一种典型的情况,其中服务器使一种特定的能力(如数据库服务)可供能够与之通信的任何客户使用。
套接字:
套接字(socket)是一个抽象层,应用程序可以通过它发送和接收数据,其方式与打开文件句柄允许应用程序读、写数据到稳定的存储器非常相似。套接字允许应用程序插入到网络中,并与插入到同一网络中的其他应用程序通信。由一台机器上的应用程序写到套接字中的信息可以被不同机器上的应用程序读取,反之亦然。
以下部分关于套接字的资料来自:https://blog.csdn.net/daaikuaichuan/article/details/83061726
套接字的类型
:流套接字(SOCK_STREAM
):提供面向连接、可靠的数据传输服务,对应TCP
。数据报套接字(SOCK_DGRAM
):数据报套接字提供了一种无连接的服务,对应UDP
。原始套接字(SOCK_RAW
)。
三种套接字的区别
:原始套接字可以读写内核没有处理的IP数据包,流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。如果要访问其他协议发送数据必须使用原始套接字。
套接字缓冲区
:
每个 socket
被创建后,都会分配两个缓冲区,输入缓冲区
和输出缓冲区
。
write()
/ send()
并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议
将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区
,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议
负责的事情。反过来想,write()
函数返回1
并不代表发送成功,只是将数据写入缓冲区写入成功了。
read()
/ recv()
函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
注:
(1)I/O缓冲区在每个TCP套接字中单独存在;
(2)I/O缓冲区在创建套接字时自动生成;
(3)即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
(4)关闭套接字将丢失输入缓冲区中的数据。
2、函数的使用:
以下大部分资料来自:http://c.biancheng.net/view/2344.html做了一些补充。
(1)socket()
函数原型:
int socket(int af,int type,int protocol);
作用:
创建套接字。
参数:
fa
:
af 为地址族(Address Family),也就是 IP 地址类型。常用的的协议簇:AF_INET(IPv4)
、AF_INET6(IPv6)
、AF_LOCAL(UNIX协议)
、AF_ROUTE(路由套接字)
、AF_KEY(秘钥套接字)
type
:
数据传输方式、套接字类型,常用的套接字的类型:SOCK_STREAM(字节流套接字、面向连接套接字)
、SOCK_DGRAM(数据报套接字、无连接的套接字)
protocol
:
标识传输协议,常用的有IPPROTO_TCP(TCP传输协议)
和IPPTPTP_UDP(UDP传输协议)
,如果此参数设为0
,系统会自动识别使用的协议:TCP
面向连接,UDP
无连接,所以系统可以通过type
参数识别协议。
使用:
//TCP (面向连接的)
int tcp_sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//UDP (无连接的)
int udp_sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
windows
中使用:
int SOCKET socket(int af, int type, int protocol);
(2)bind()
函数原型:
int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
作用:
套接字绑定IP和端口。
参数:
sock
:
socket
函数返回的套接字描述符。
addr
:
是指向本地IP
地址的结构体指针。
addrlen
:
结构长度。
结构体:
struct sockaddr{unsigned short sa_family; //通信协议类型族AF_xxchar sa_data[14]; //14字节协议地址,包含该socket的IP地址和端口号};
struct sockaddr_in{short int sin_family; //通信协议类型族unsigned short int sin_port; //端口号struct in_addr sin_addr; //IP地址unsigned char si_zero[8]; //填充0以保持与sockaddr结构的长度相同};
struct in_addr{in_addr_t s_addr; //32位的IP地址
};
sockaddr
和 sockaddr_in
的长度相同,都是16
字节,只是将IP
地址和端口号合并到一起,用一个成员 sa_data
表示。要想给 sa_data
赋值,必须同时指明IP
地址和端口号。sockaddr
是一种通用的结构体,可以用来保存多种类型的IP
地址和端口号,而 sockaddr_in
是专门用来保存 IPv4
地址的结构体。
用来存放ipv6
地址的结构体:
struct sockaddr_in6 {sa_family_t sin6_family; //(2)地址类型,取值为AF_INET6in_port_t sin6_port; //(2)16位端口号uint32_t sin6_flowinfo; //(4)IPv6流信息struct in6_addr sin6_addr; //(4)具体的IPv6地址uint32_t sin6_scope_id; //(4)接口范围ID
};
使用时的代码:
//创建套接字
int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//创建sockaddr_in结构体变量
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址,本地
serv_addr.sin_port = htons(8000); //端口
//将套接字和IP、端口绑定
bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
windows
中使用:
int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);
(3)connect()
函数原型:
int connect(int sock,struct sockaddr *serv_addr,socklen_t addrlen);
作用:
建立到达服务器的连接。
参数:(参数与bind()
一样)
sock
:
socket
函数返回套接字描述符。
serv_addr
:
服务器IP
地址结构指针。
addrlen
:
结构体指针的长度。
windows
中使用:
int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
(4)listen()
函数原型:
int listen(int sock, int backlog)
作用:
通过isten()
函数可以让套接字进入被动监听状态。
参数:
sock
:
sock
为需要进入监听状态的套接字。
backlog
:
设置可连接客户端的最大连接个数,即请求队列。当有多个客户端向服务器请求时,收到此值的影响。当设该参数为SOMAXCONN
时,会让系统来决定请求队列的长度。
被动监听:
指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。
请求队列:
当套接字正在处理客户端请求时,如果有新的请求进来,套接字是没法处理的,只能把它放进缓冲区,待当前请求处理完毕后,再从缓冲区中读取出来处理。如果不断有新的请求进来,它们就按照先后顺序在缓冲区中排队,直到缓冲区满。这个缓冲区,就称为请求队列(Request Queue)
。
当请求队列满时,就不再接收新的请求,对于 Linux
,客户端会收到 ECONNREFUSED
错误,对于 Windows
,客户端会收到 WSAECONNREFUSED
错误。
windows中的使用:
int listen(SOCKET sock, int backlog);
注意:
listen()
只是让套接字处于监听状态,并没有接收请求。接收请求需要使用 accept()
函数。
(5)accept()
函数原型:
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
作用:
当套接字处于监听状态时,可以通过 accept()
函数来接收客户端请求。
参数:
sock
:
服务器端套接字。
addr
:
sockaddr_in
结构体变量。
addrlen
:
参数 addr
的长度,可由 sizeof()
求得。
windows中使用:
SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
注:
listen()
只是让套接字进入监听状态,并没有真正接收客户端请求,listen()
后面的代码会继续执行,直到遇到 accept()
。accept()
会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。
(6)write()
函数原型:
ssize_t write(int fd, const void *buf, size_t nbytes);
作用:
向套接字中写入数据。
参数:
fd
:
要写入的文件的描述符。
buf
:
要写入的数据的缓冲区地址。
nbytes
:
要写入的数据的字节数。
注:
两台计算机之间的通信相当于两个套接字之间的通信,在服务器端用 write()
向套接字写入数据,客户端就能收到,然后再使用 read()
从套接字中读取出来,就完成了一次通信。
windows中的使用:
windows
下使用的是send()
。
int send(SOCKET sock, const char *buf, int len, int flags);
(7)read()
函数原型:
ssize_t read(int fd, void *buf, size_t nbytes);
作用:
read()
函数会从 fd
文件中读取 nbytes
个字节并保存到缓冲区buf
,成功则返回读取到的字节数(但遇到文件结尾则返回0
),失败则返回 -1
。
参数:
fd
:
要读取的文件的描述符。
buf
:
要接收数据的缓冲区地址。
nbytes
:
要读取的数据的字节数。
windows中的使用:
windows
下使用的是recv()
。
int recv(SOCKET sock, char *buf, int len, int flags);
(8)inet_addr()
函数原型:
in_addr_t inet_addr(const char* strptr);
作用:
将字符串形式的IP地址,转为网络字节序的整型。
参数:
字符串型地址,如:“192.168.1.5”
。
(9)inet_ntoa()
函数原型:
char *inet_ntoa(struct in_addr in);
作用:
将网络字节序的整型值转为字符串型的IP
地址。
(10)memset()
函数原型:
extern void *memset(void *buffer, int c, int count)
作用:
使用某个值填充数组或指针。
参数:
buffer
:
等待填充的数组或指针。
c
:
填充的值。
count
:
buffer
中填充的长度。
使用:
//把buffer数组,所有元素置0
memset(buffer, 0, sizeof(buffer))
linux c socket编程学习(1)(一些基本概念及基本的函数使用)相关推荐
- asp.core api 通过socket和服务器通信发送udp_详解Linux的SOCKET编程
文章来自于 https://www.zhangshengrong.com/p/9Oabd95XdK/ PHP进阶学习交流QQ群:983229225 本篇文章对Linux的SOCKET编程进行了详细解释 ...
- socket编程学习(addr设置)
sockaddr结构体 struct sockaddr{ sa_family_t sa_family; //地址族,最常用的是"AF_INET"(IPV4)和"AF_ ...
- Linux下Socket编程
Linux下Socket编程 网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...
- linux系统udp通信程序,Linux UDP socket编程(UDP通讯模型) | C/C++程序员之家
Linux UDP socket编程(UDP通讯模型): UDPClient + UDPService. Linux下大多数网络程序都是基于TCP的,很少基于UDP,简单的通讯模型如下,开发时候备用! ...
- Linux的SOCKET编程 简单演示
转载:http://blog.csdn.net/hguisu/article/details/7445768/ Linux的SOCKET编程详解 1. 网络中进程之间如何通信 进 程通信的概念最初来源 ...
- 一文了解linux下socket编程
一文了解linux下socket编程 文章目录 一文了解linux下socket编程 1 网络编程的相关简述 1.1 引言 1.2 Tcp和Udp简介 1.3 TCP三次握手和四次挥手 1.4 网络编 ...
- LINUX下Socket编程 函数格式详解
你需要了解的一些系统调用: socket() bind() connect() listen() accept() send() recv() sendto() recvfrom() close() ...
- Linux的SOCKET编程详解
Linux的SOCKET编程详解 一. 网络中进程之间如何通信 进程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统 ...
- linux下socket编程中setsockopt的作用
如题所示,在linux进行socket编程的时候,一般而言,socket,bind,listen三步曲之后,就开始接收客户端请求,然后实现收发数据. 如下所示的代码,是没有setsockopt的情况: ...
最新文章
- 希尔排序——算法系列
- java获取当前路径
- 详解|清华大学100页PPT:工业机器人技术详解
- hdu1863 畅通工程---MST连通
- perl 取出目录中的所有文件并存入文件中
- 对oracle静态参数修改一点研究
- 五分钟了解先验概率和后验概率
- FreeBSD与Linux的比较
- 加减法叫做什么运算_【课堂实录】加减法运算的本质(四上)
- CCPC-Wannafly Winter Camp Day3 (Div2, onsite) I 石头剪刀布(按秩合并并查集)
- [KALI] 开启ssh远程连接
- Cisco路由器配置静态路由
- Java 后台做图片压缩的两种方法
- java-IO-字节数组输入输出流(ByteArrayInputStream、ByteArrayOutputStream)
- 3D游戏——AR图片识别与建模
- C 实现Mysql增量备份_mysql增量备份 - lyle_luo的个人页面 - OSCHINA - 中文开源技术交流社区...
- Nexus搭建Maven私服全攻略一:认识Nexus与索引
- Python与rrdtool的结合模块
- 【GDOI 2016 Day1】疯狂动物城
- c语言托儿所收2到6岁儿童,2018下教师资格考试测试试题:幼儿《保教知识与能力》(三)...
热门文章
- 云和恩墨大讲堂 - 海南站
- 论文笔记-DeepLung: Deep 3D Dual Path Nets for Automated Pulmonary Nodule Detection and Classification
- 天气预报查询数据接口、实时天气、24小时天气、未来24小时、7天/15天预报
- [转]多媒体封装格式详解---MP4
- SpringBoot整合Redis实现缓存、队列、广播
- 不是所有产品都适合做亚马逊CPC广告
- 移动H5前端性能优化指南(转自ISUX)
- 3d打印机打印obj格式_购买3D打印机如何为您省钱
- 实例6:圆周率的计算
- List size为1但是内容为null处理方法