网络编程-套接字(scoket)
2016-08-07 17:00
316 查看
socket编程
socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket。
在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。socket本身有“插座”的意思,因此用来描述网络连接的一 对一关 系。 TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。
在网络中,必须保证数据是大端存储。必须从低地址处发送,数据是高位的。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6、UNIX Domain Socket。然而,各种网络协议的地址格式并不相同,如下图所⽰示: sockaddr数据结构
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sockaddr_un结构体表示。各种socket地址结构体的开头都是相同的,前16位表示整个结构体的长度(并不是所有 UNIX的实现
都有长度字段,如Linux就没有),后16位表示地址类型。IPv4、IPv6和UNIX Domain Socket的地址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。因此,socket API可以接受各种类型的sockaddr结构体指针做参数,例如bind、accept、connect等函数,这些函数的参数应该设计成void
*类型以便接受各种类型的指针,但是sock API的实现早于ANSI C标准化,那时还没有void *类型,因此这些函数的参数都用struct sockaddr *类型表示,在传递参数之前要强制类型转换一下。
基于TCP协议的网络协议的一般流程:
创建套接字(scoket)
int scoket(AF_INET,SOCK_STREAM,0)
返回值: 失败返回-1;
成功返回文件描述符;
绑定套接字
int bind(listen_sock,(struct sockaddr*)&local,sizeof(local));
返回值:
失败返回-1;
设置套接字的状态(监听状态)
int listen(int scokfd,5);
返回值:
失败返回-1;
accept链接
成功就返回一个新建的scoket的描述符;
服务器调用socket()、bind()、listen()
完成初始化后,调用accept()阻塞等待,处于监听端口的状 态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从 accept()返回。
数据传输的过程: 建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从 accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送 请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
如果客户端没有更多的请求了,就调用close() 关闭连接,就像写端关闭的管道一样,服务器的 read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close() 后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown() 则连接处于半关闭状态,仍可接收对方发来的数据。
基于TCP协议的单进程客户端/服务器的程序:
运行结果:
基于TCP协议的多进程客户端/服务器的程序:
运行结果:
基于TCP协议的多线程客户端/服务器的程序:
运行结果:
下面来看一个错误:
这是为什么呢?
虽然server的应用程序终止了,但TCP协议层的连接并没有 完全断开,因此不能再次监听同样的server端口。
server的TCP连接收到client发的FIN段后处于TIME_WAIT状 态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime) 的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所 以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端 口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后
就可以再次启动server 了。
如何解决:
使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1, 表⽰允许创建端口号相同但IP地址不同的多个socket描述符。
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket。
在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。socket本身有“插座”的意思,因此用来描述网络连接的一 对一关 系。 TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。
在网络中,必须保证数据是大端存储。必须从低地址处发送,数据是高位的。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6、UNIX Domain Socket。然而,各种网络协议的地址格式并不相同,如下图所⽰示: sockaddr数据结构
IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sockaddr_un结构体表示。各种socket地址结构体的开头都是相同的,前16位表示整个结构体的长度(并不是所有 UNIX的实现
都有长度字段,如Linux就没有),后16位表示地址类型。IPv4、IPv6和UNIX Domain Socket的地址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。因此,socket API可以接受各种类型的sockaddr结构体指针做参数,例如bind、accept、connect等函数,这些函数的参数应该设计成void
*类型以便接受各种类型的指针,但是sock API的实现早于ANSI C标准化,那时还没有void *类型,因此这些函数的参数都用struct sockaddr *类型表示,在传递参数之前要强制类型转换一下。
基于TCP协议的网络协议的一般流程:
创建套接字(scoket)
int scoket(AF_INET,SOCK_STREAM,0)
返回值: 失败返回-1;
成功返回文件描述符;
绑定套接字
int bind(listen_sock,(struct sockaddr*)&local,sizeof(local));
返回值:
失败返回-1;
设置套接字的状态(监听状态)
int listen(int scokfd,5);
返回值:
失败返回-1;
accept链接
成功就返回一个新建的scoket的描述符;
服务器调用socket()、bind()、listen()
完成初始化后,调用accept()阻塞等待,处于监听端口的状 态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从 accept()返回。
数据传输的过程: 建立连接后,TCP协议提供全双工的通信服务,但是一般的客户端/服务器程序的流程是由客户端主动发起请求,服务器被动处理请求,一问一答的方式。因此,服务器从 accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送 请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。
如果客户端没有更多的请求了,就调用close() 关闭连接,就像写端关闭的管道一样,服务器的 read()返回0,这样服务器就知道客户端关闭了连接,也调用close()关闭连接。注意,任何一方调用close() 后,连接的两个传输方向都关闭,不能再发送数据了。如果一方调用shutdown() 则连接处于半关闭状态,仍可接收对方发来的数据。
基于TCP协议的单进程客户端/服务器的程序:
运行结果:
基于TCP协议的多进程客户端/服务器的程序:
运行结果:
基于TCP协议的多线程客户端/服务器的程序:
运行结果:
下面来看一个错误:
这是为什么呢?
虽然server的应用程序终止了,但TCP协议层的连接并没有 完全断开,因此不能再次监听同样的server端口。
server的TCP连接收到client发的FIN段后处于TIME_WAIT状 态。TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL(maximum segment lifetime) 的时间后才能回到CLOSED状态,因为我们先Ctrl-C终止了server,所 以server是主动关闭连接的一方,在TIME_WAIT期间仍然不能再次监听同样的server端 口。MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Linux上一般经过半分钟后
就可以再次启动server 了。
如何解决:
使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1, 表⽰允许创建端口号相同但IP地址不同的多个socket描述符。
int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
相关文章推荐
- java-模拟tomcat服务器
- Linux socket 初步
- java socket 注意的地方
- java socket 注意的地方
- C#基于socket模拟http请求的方法
- Lua下基本的网络编程示例
- 简单的Ruby中的Socket编程教程
- Socket不能选择本地IP连接问题如何解决
- C#之Socket操作类实例解析
- 使用C#来编写一个异步的Socket服务器
- C#使用Socket快速判断数据库连接是否正常的方法
- TCP版backshell的VBS脚本代码
- 科学知识:理解socket
- websocket++简单使用及实例分析
- linux网络编程用到的网络函数详解用和使用示例
- Android聊天工具基于socket实现
- PHP socket 模拟POST 请求实例代码
- php与flash as3 socket通信传送文件实现代码
- 解决time_wait强制关闭socket
- C#网络编程基础之进程和线程详解