Socket讲解
服务端客户端通信例子:socket tcp 通信1,socket tcp通信2,udp使用讲解,socket udp通信例子
内容:
一、简述
二、创建套接字
三、服务器
四、客户端
五、数据传输
六、关闭连接
注:可以略过简述,直接看socket的讲解/编程
使用socket进行TCP通信时,经常使用的函数有:将从 二、创建套接字讲起
一、简述
1. 不同主机进程间的通信——Socket
1)网络中的数据传输是一种I/O操作
2)read、write、close操作可应用于Socket描述符
3)Socket是一种文件描述符,代表了一个通信管道的一个端点
4)在Socket类型的文件描述符上,可以完成建立连接,数据传输等操作
5)常用的Socket类型有两种:
流式Socket:SOCK_STREAM , 提供面向连接的Socket
数据报式Socket:SOCK_DGRAM , 提供面向无连接的Socket
2. 字节序
1)概念:是指多字节数据的存储顺序
2)分类:
小端格式:将低位字节数据存储在低地址
大端格式:将高位字节数据存储在低地址
3)特点:
网络协议指定了通讯字节序--大端
只有在多字节数据处理时才需要考虑字节序
运行在同一台计算机上的进程相互通信时,一般不用考虑字节序
异构计算机之间通讯,需要转换自己的字节序为网络字节序
4)确定主机字节序:
5)字节序转换函数
1)主机字节序数据转换成网络字节序数据
uint16_t htons(uint16_t host16bit) 把16位值从主机字节序转到网络字节序
uint32_t htonl(uint32_t host32bit) 把32位值从主机字节序转到网络字节序
功能:
将32或16位主机字节序数据转换成网络字节序数据
参数:
uint32_t: unsigned int
hostint32:待转换的32位主机字节序数据
返回值:
成功:返回网络字节序的值
2)网络字节序数据转换成主机字节序数据
uint16_t ntohs(uint16_t net16bit) 把16位值从网络字节序转到主机字节序
uint32_t ntohs(uint32_t net32bit) 把32位值从网络字节序转到主机字节序
功能:
将32或16位网络字节序数据转换成主机字节序数据
参数:
uint32_t: unsignedint
netint32: 待转换的32位网络字节序数据
返回值:
成功:返回主机字节序的值
3. 通用套接字地址结构sockaddr
套接字数据结构用于保存套接字信息,与使用该结构的网络协议有关,每一种网络协议都有其本身的网络地址数据结构,都是以sockaddr_开头的,不同的网络协议有不同的后缀,如IPv4对应的是skocaddr_in
1)地址标识了特定通信域中的套接字端点,
地址格式与特定通信域相关,
为使不同格式地址能被传入套接字函数,地址被强制转换成通用套接字地址结构
通用套接字地址结构如下:
struct sockaddr{sa_family_t sa_family; //2 字节,地址族 AF_xxx
char sa_data[14]; //14 字节的协议地 址,包含套接字IP和端口号};
头文件:#include <netinet/in.h>
4. 套接字地址结构sockaddr_in
1)在IPv4因特网域(AF_INET)中,套接字地址结构用sockaddr_in命名、
struct sockaddr_in
{
sa_family_t sin_family; //2字节,地址族
in_port_t sin_port; //2字节,端口号
struct in_addr sin_addr; //4字节,IP地址
unsigned char sin_zero[8]; //8字节,
};
struct in_addr
{
in_addr_t s_addr; //4字节
};
sin_zero说明:用来将sockaddr_in结构填充到与sockaddr同样长度,可用bzero()或memset()函数将其置为0
头文件:#include <netinet/in.h>
5. 地址转换函数
1)int inet_pton(int family, const char* strptr,void *addrptr);
功能:
将点分十进制数串转换成32位无符号整数
参数:
family 协议族
strptr 点分十进制数串
addrptr 32位无符号整数的地址
返回值:
成功:1
失败:其它
头文件:#include <arpa/inet.h>
2)const char *inet_ntop(int family, const void* addrptr, char *strptr, size_t len);
功能:
将32位无符号整数转换成点分十进制数串
参数:
family 协议族
addrptr 32位无符号整数
strptr 点分十进制数串
len strptr缓存区长度
len的宏定义
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46 //for ipv6
返回值:
成功:则返回字符串的首地址
失败:返回NULL
头文件:#include <arpa/inet.h>
6.服务模型
TCP:
Udp:
二、创建套接字
创建套接字是进行任何网络通信时必须做的第一步
1.int socket(int family, int type,int protocol);
功能:
创建一个用于网络通信的I/O描述符(套接字)
参数:
family:协议族
AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY
常用值 AF_INET 互联网协议族
type:套接字类型
SOCK_STREAM(流式套接字)
SOCK_DGRAM(数据包套接字)
SOCK_RAW (原始套接字)
SOCK_SEQPACKET
protocol:协议类别
0,IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP
常用值 0
返回值:套接字
socket创建的套接字特点
使用socket创建套接字时,系统不会分配端口
使用socket创建的是主动套接字,但作为服务器,
需要被动等待别人的连接
头文件:#include<sys/socket.h>
三、服务端
做为服务器需要具备的条件
1)具备一个可以确知的地址,以便让别人找到我
2)让操作系统知道你是一个服务器,而不是一个客户端
3)等待连接的到来
对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始
1.int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能:
将本地协议地址与sockfd绑定,套接字配置
参数:
sockfd: socket套接字
myaddr: 指向特定于协议的地址结构指针,
addrlen:该地址结构的长度
返回值:
成功:返回0
失败:其他
头文件:#include<sys/socket.h>
myaddr说明:
myaddr其实是指向含有本机IP地址及端口号等信息的sockaddr类型的指针
my_addr.sin_port= 0;//系统随机选择一个未使用的端口
my_addr.sin_addr= INADDR_ANY;//填入本机IP地址
注意:在使用bind函数使,需要将sin_port转换为网络字节优先
注意:INADDR_ANY 通配地址,值为0
2. int listen(int sockfd, int backlog);
功能:
listen使套接字处于被动监听模式
使操作系统为该套接字设置一个连接队列,来记录所有连接到该套接字的连接
参数:
sockfd: socket监听套接字
backlog:连接队列的长度,输入队列已满时,将拒绝连接请求
返回值:
成功:返回0
失败:其他
头文件:#include <sys/socket.h>
4.int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
功能:
从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待
在建立好输入队列后,服务器就调用accept函数,然后进入睡眠并等待客户端的连接请求
收到连接请求时,sockfd将将建立一个新的套接字,并把新套接字和请求连接进程的地址联系起来,收到服务套接字的初始套接字仍可以继续在以前的套接字上监听,同时也可以在新的套接字描述符上进行数据传输
参数:
sockfd: socket监听套接字
cliaddr: 用于存放客户端套接字地址结构
addrlen:套接字地址结构体长度
返回值:已连接套接字
注意:accept函数返回的是一个已连接套接字,这个套接字代表当前这个连接
头文件:#include <sys/socket.h>
四、客户端
作为客户端需要具备的条件:知道服务器的IP地址以及端口号
1.int connect(int sockfd,const struct sockaddr* addr,socklen_t len);
功能:
主动跟服务器建立链接
连接建立成功后才可以开始传输数据(对于TCP协议)
用于tcp客户端,udp不使用
参数:
sockfd:socket套接字
addr: 需连接的服务器地址结构
addrlen:地址结构体长度
返回值:
成功:0
失败:其他
注意:connect函数建立连接之后不会产生新的套接字
头文件:#include <sys/socket.h>
例子:
五、数据传输
当连接建立后,通信的两端便具备两个套接字
套接字也是一种文件描述符,所以read、write函数可以用于从这个连接中取出或向其写入数据
1.ssize_t send(int sockfd, const void* buf,size_t nbytes, int flags);
功能:
用于发送数据
注意:不能用TCP协议发送0长度的数据包
参数:
sockfd: socket套接字
buf: 待发送数据缓存区的地址
nbytes: 发送缓存区大小(以字节为单位)
flags: 套接字标志(常为0)
返回值:成功发送的字节数
头文件:#include <sys/socket.h>
注意:send发送的字节数可能和请求的字节数不一致,或小或大
2.ssize_t recv(int sockfd, void *buf,size_t nbytes,int flags);
功能:
用于接收网络数据
参数:
sockfd: 套接字
buf: 指向接收网络数据的缓冲区
nbytes: 接收缓冲区的大小(以字节为单位)
flags: 套接字标志(常为0)
返回值:成功接收到字节数
头文件:#include <sys/socket.h>
六、关闭连接
1. int close(int fd);
参数:fd是调用socket函数返回的套接字描述符
头文件:#include<unistd.h>
2. int shutdown(int s,int how)
功能:
允许只停止某个方向的数据传输,另一个方向的继续传输
参数:
s 需要关闭的套接字描述符
how:
0 停止继续接收数据
1 停止继续发送数据
2 停止发送和接收数据
返回值:
成功 0
失败 -1
头文件: #include <sys/socket.h>
3.使用close函数即可关闭套接字
1)关闭一个代表已连接套接字,将导致另一端接收到一个0长度的数据包
2)做服务器时
关闭socket创建的监听套接字,将导致服务器无法继续接受新的连接,但不 会影响已经建立的连接
关闭accept返回的已连接套接字,将导致它所代表的连接被关闭,但不会影 响服务器的监听
3)做客户端时
关闭连接就是关闭连接,不意味着其他