Linux 网络编程二(Socket创建)
TCP通信
一个程序使用套接字需要执行4个步骤。
--分配套接口和初始化
--连接
--发送或接收数据
--关闭套接字
涉及到的调用包括socket、bind、listen、connect(阻塞线程)、accept(阻塞线程)、recv(阻塞线程)、send(阻塞线程)。
分配套接口和初始化
--我们需要做的第一件工作就是分配套接口。
--套接口可以看作是文件描述符
--不论server端,还是client端,第一步都是一样的
每个套接口都是一个通信的通道
两个进程通过套接口建立连接后就可以发送和接收数据了。
socket()
int socket(int domain.int tyoe,int protocol);
系统调用socket带有以下参数
--int domain
--int tyoe
--int protocol(这个值一般都取0)
--成功返回套接字描述符,失败返回-,并设置errno
socket参数
domain说明
AF_UNIX UNIX内部使用
AF_INET TCP/IP协议
AF_ISO 国际标准组织协议
AF_NS Xerox网络协议 type说明
SOCK_STREAM 使用TCP可靠连接
SOCK_DGRAM 使用UDP不可靠连接
在写网络程序的时候,建立TCP socket:sock = socket(PF_INET, SOCK_STREAM, );然后再绑定本地地址或连接远程地址时需要初始化sockaddr_in结构,
其中指定address family时一般设置问AF_INET,即使用IP。
相关的头文件定义:
AF = Address Family
PF = Protocol Family
AF_INET = PF_INET
所以在windows中,AF_INET与PF_INET完全一样,而在Unix/Linux系统中,在不同的版本中这两者有微小差别。
对于BSD,是AF,对于POSIX是PF。理论上,建立socket时是指定协议,应该用PF_XXXX,设置地址时应该用AF_XXXX。
当然AF_INET和PF_INET的值是相同的,混用也不会有太大的问题。
bind()函数
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
bind将进程和一个套接口联系起来,bind通常用于服务器进程为接入客户连接建立一个套接口(简单而言,就是把程序和一个IP地址端口号绑定在一起)。
参数sockfd是函数socket调用返回的套接口。
参数my_addr是结构sockaddr的地址(用来描述IP地址的一个结构)。
参数addrlen设置了my_addr能容纳的最大字节数。
成功返回0,失败返回-,并设置errno。
一个端口号只能绑定一个程序,1对1关系
socklen_t本质上是unsigned int,并不是int,在windows操作系统下才是int。
INADDR_ANY是ANY,是绑定地址0.0.0.0上的监听, 能收到任意一块网卡的连接;
INADDR_LOOPBACK, 也就是绑定地址LOOPBAC, 往往是127.0.0., 只能收到127.0.0.1上面的连接请求
对于客户端程序,下一步是要与之通信的服务器建立连接。
--客户端只需使用connect即可
对于服务端程序,就是要建立自己的套接口等待来自客户端的连接。
--服务器需要调用listen和accept两个函数。
listen()函数
int listen(int sockfd,int backlog);
创建了套接口并且使用bind将它和一个进程关联起来以后,服务端就需要调用listen来监听指定端口的客户端连接。
参数sockfd是调用socket返回的套接口描述符
参数backlog设置接入队列的大小,通常把这个值设置的足够大就可以了。
参数backlog一般用来设置服务端可以并发的接收客户端的最大连接数目
listen也是从TCP缓存中读取连接,并非来一个接收一个。
成功返回0,失败返回-,并设置errno
listen是将连接直接放到缓存区里,等待accept函数来接收
accept()函数
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
当有客户端连接到服务端,他们会排入队列,知道服务端准备好处理他们位置为止,accept会返回一个新的套接口,同时原来的套接口继续listen指定端口号
参数sockfd是调用socket返回的套接口描述符
参数addr指向结构sockaddr地址,表示客户端的IP地址。
参数addrlen设置了addr能容纳的最大字节数。
成功返回新的套接字,失败返回-,并设置errno
connect()函数
int connect(int sockfd,const struct sockaddr * serv_addr,socklen_t addrlen);
客户端调用connect与服务端进行连接。
参数sockfd是调用socket返回的套接口描述符。
参数addr指向结构sockaddr地址。
参数addrlen设置了addr能容纳的最大字节数。
成功返回0,失败返回-,并设置errno。
connect()函数也是阻塞的,它必须完成三次握手机制才能返回。
客户端和服务端建立了连接就可以在客户端和服务端之间传输数据了,需要两个系统调用。
--send 发送数据。
--recv 接收数据。
一个套接口既可以发送数据,也可以接收数据,网络是一个双向管道。
send()函数
ssize_t send(int s,const void *buf,size_t len,int flags);
send函数用来发送数据。
参数s是已经建立连接的套接口。
参数buf是发送数据内存buffer地址指针。
参数len指明buffer的大小,单位字节。
参数flags一般填0.
成功返回发送的字节数,失败返回-,并设置errno。
send()函数注意点
.send返回值理解
send在阻塞场景下,返回值要么是指定长度(发送成功),要么是-,发送失败,但是在非阻塞场景下,
返回值可能小于指定长度,这是因为发送数据超过发送缓冲区(窗口),所以只能发送缓冲区大小的数据,剩下的数据无法发送
2.errno=11的理解
send在非阻塞场景可能返回-,并且更新errno为11,11表示资源临时不可用,当发送缓冲区满了,
而程序不断在调用send(0函数发送数据就会出现这个错误,此时收到返回值为-,并且errno=11时,需要停止发送数据,等待套接字下次可写的时候再发送数据。
recv()函数
ssize_t recv(int s,void *buf,size_t len,int flags);
recv函数用来接收数据。
参数s是已经建立连接的套接口。
参数buf是接收数据内存buffer地址指针。
参数len指明buffer的大小,单位字节。
参数flags一般填0.
成功返回接收到的字节数,失败返回-1,并且设置errno,如果对端套接字已经关闭,返回0.
recv函数只是从TCP缓存中读数据(此时数据已经在自己的电脑上了),不是直接从网络中读数据,什么时候TCP缓存区满了,另一边的send函数才会停止发送数据。
recv()函数会阻塞线程,直到收到消息或者客户端关闭。
ssize_t recv(int s, void *buf, size_t len, int flags);
参数flags的值为 MSG_PEEK 介绍
当flags的值设置为0,此时recv函数将读取 socket 缓冲区中的数据到 buf 中,并且会移除 socket 缓冲区中已读取的数据
当flags的值设置为MSG_PEEK,此时recv函数将读取 socket 缓冲区中的数据到 buf 中,但是不会移除 socket 缓冲区中已读取的数据(偷看一下缓冲区中数据)
最后当你用完套接口以后,就该释放套接口所占用的资源了,通过close做到这一点
当试图向一个已经关闭的套接口写或者读数据就会出错。
setsockopt()函数
int setsockopt(int s,int level,int optname,const void *optval,socklen optlen);
setsockopt函数设置套接口
函数成功返回0,失败返回-1,并设置errno
常见用法为:
int on=;
setsockopt(st,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
由于TCP套接字状态TIME_WAIT引起该套接字关闭后约保留2到4分钟。在此期间bind绑定该端口失败。
SO_REUSEADDR指示系统地址可重用。
当服务器在listen()函数后直接退出,如果这时候有一个客户端来连接,这个连接会被放在缓存中,如果这时候启动了一个新的程序绑定这个IP地址和端口号,
那么这新的程序就会接收到上一个客户端的请求,这是有问题,因为上一个客户端请求访问的是原始程序。但是如果我们就一直一个程序绑定这个IP地址,就用不着这个TIME_WAIT信号了,
我们服务器上就一个server绑定这个IP地址和端口号,所以用不着这个机制,因此调用setsockopt()函数。
IP地址的结构
ip地址在内存中用int表示,int在内存中占有4个字节的空间
第一个字节:
第二个字节:
第三个字节:
第四个字节:
send()、recv()函数原理解析
通过上一章网络编程一中的主机之间的通讯图可以得知,两主机之间通信,主机A先将消息打包成TCP包,TCP包再打包成IP包,然后形成以太网包发送,
另一台主机的接收顺序恰好相反,先接受以太网包,再接收IP包,最后接收TCP包,通过程序中测试,recv()函数一次能够接受的数据(red hat中最大能接收64K数据)要
比send()函数发送的数据(red hat中最大发送的数据超过128K),send()函数是将数据打包成TCP包再发送,而接收数据的时候是接收的IP包,再将IP包还原成TCP包,
这说明TCP包的容量实际上会大于或者等于IP包,事实上计算机在发送数据时,如果TCP包过大,会把TCP拆解成多个IP包,并将这些IP包存储在网卡的缓存区里(如果send
发送数据超过缓冲区,那么sned()所在的线程就会被挂起),recv在网卡上也有缓存区,网络发送过来的数据会先存放在缓存区中,直到缓存区被填满,此时发送方就会停
止发送,但是此时不意味这send函数所在的线程就会被挂起,send函数还可以往自己的缓存区中发数据,直到缓存区填满。
在linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),
这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,
这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。
对非阻塞socket而言,EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK。
另外,如果出现EINTR即errno为4,错误描述Interrupted system call,该错误是由于操作被信号打算,操作也应该继续。
最后,如果recv的返回值为0,那表明连接已经断开,我们的接收操作也应该结束。
Linux 网络编程二(Socket创建)的更多相关文章
- Linux网络编程(二)
Linux网络编程(二) 使用多进程实现服务器并发访问. 采用多进程的方式实现服务器的并发访问的经典范例. 程序实现功能: 1.客户端从标准输入读入一行文字,发送到服务器. 2.服务器接收到客户端发来 ...
- linux网络编程-(socket套接字编程UDP传输)
今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中, ...
- linux网络编程(socket)之面向连接(TCP/IP)
1.流程 服务器: 创建socket: 绑定端口: 监听: 监听到有连接请求,接受请求: 建立连接,开始对话. 客户端: 创建socket: 请求建立连接: 连接建立成功,开始对话. 2.实例代码 / ...
- Linux网络编程3——socket
宏定义 首先介绍两个宏定义,看如下代码 代码1 /************************************************************************* & ...
- Linux网络编程二、tcp连接API
一.服务端 1.创建套接字: int socket(int domain, int type, int protocol); domain:指定协议族,通常选用AF_INET. type:指定sock ...
- Linux网络编程:socket文件传输范例
基于TCP流协议的socket网络文件传输Demo: 实现:C语言功能:文件传输(可以传任何格式的文件) /********************************************** ...
- Linux网络编程:UDP Socket编程范例
TCP协议提供的是一种可靠的,复杂的,面向连接的数据流(SOCK_STREAM)传输服务,它通过三段式握手过程建立连接.TCP有一种"重传确认"机制,即接收端收到数据后要发出一个肯 ...
- Linux网络编程socket选项之SO_LINGER,SO_REUSEADDR
from http://blog.csdn.net/feiyinzilgd/article/details/5894300 Linux网络编程中,socket的选项很多.其中几个比较重要的选项有:SO ...
- Linux网络编程(三)
Linux网络编程(三) wait()还是waitpid() Linux网络编程(二)存在客户端断开连接后,服务器端存在大量僵尸进程.这是由于服务器子进程终止后,发送SIGCHLD信号给父进程,而父进 ...
随机推荐
- 【原】iOS下KVO使用过程中的陷阱
KVO,全称为Key-Value Observing,是iOS中的一种设计模式,用于检测对象的某些属性的实时变化情况并作出响应.网上广为流传普及的一个例子是利用KVO检测股票价格的变动,例如这里.这个 ...
- IOS组件绑定无效错误
报错的原因:界面按钮事件没有绑定到源代码或者相关的代码被注释了.比如你的button组件以及绑定到IBOutlet,但是viewcontrol.m上没有相关的代码,就会出现异常.
- 关于手机微网站ICP备案
今天终于拨通了陕西省通信管理局的电话,并告诉对方我们做的是一个化妆品的微网站,会涉及到使用使用支付宝支付. 询问"xxx微网站"网站经营类型,对方告知虽然使用支付宝,但是是微网站, ...
- CentOS 6.4安装配置LNMP服务器(Nginx+PHP+MySQL)
准备篇 1.配置防火墙,开启80端口.3306端口 vi /etc/sysconfig/iptables-A INPUT -m state --state NEW -m tcp -p tcp --dp ...
- Apache Kafka - Quick Start on Windows
在这篇文章中,我将要介绍如何搭建和使用Apache Kafka在windows环境.在开始之前,简要介绍一下Kafka,然后再进行实践. Apache Kafka Kafka是分布式的发布-订阅消息的 ...
- IE6/7/8不支持jQuery创建非闭合格式的链接A
代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <scri ...
- POJ 2513 Colored Sticks(欧拉回路,字典树,并查集)
题意:给定一些木棒,木棒两端都涂上颜色,求是否能将木棒首尾相接,连成一条直线,要求不同木棒相接的一边必须是相同颜色的. 无向图存在欧拉路的充要条件为: ① 图是连通的: ② 所有节 ...
- web.xml文件报错:cvc-complex-type.2.4.a: Invalid content was found starting with element 'init-param'.
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" ...
- 虚拟机下Ubuntu没有GUI图形界面,解决方法
先说下快捷键,CLI切换到GUI:Ctrl+Alt+F7: GUI切换到CLI:Ctrl+Alt+F1. 今天折腾虚拟机时,打开Ubuntu后显示的是命令行界面,按快捷键后并没转换到图形界面,而是一直 ...
- Hbase step by step 完全分布式安装
Step1: download and extract the packages: http://mirror.bit.edu.cn/apache/hbase/stable/ Step2: set t ...