socket套接字TCP API
socket概念
- socket又称“套接字”,是计算机网络中进程间通信数据通道的一个端点。或称之为句柄。IP地址+port号就能够唯一确定一个socket。
- TCP/IP协议族包含传输层(TCP/UDP),网络层(ICMP/IP/IGMP),链路层(ARP/RARP)。应用层通常使用socket地址。即IP地址+port号来确定通信的对端。而socket正是TCP/IP协议族与应用层之间的接口层,能够说对上层提供了TCP/IP协议族的一种封装。无需关心更底层的实现。
- 应用上通常使用一些更高层的协议库来编程。socket很多其它归类于底层驱动编程。
只是熟悉socket总归是有优点的。
套接字地址结构
- IP地址+port号能够唯一确定一个socket套接字地址,命名为
sockaddr_in
。位于netinet/in.h
头文件里。定义例如以下。struct in_addr{ in_addr_t s_addr; }; struct sockaddr_in{ uint8_t sin_len; sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr; char sin_zero(0); };
-
in_addr_t
in_port_t
位于netinet/in.h
-
in_addr_t
一般定义为uint32_t
,in_port_t
一般定义为uint16_t
-
sa_family_t
位于sys/socket.h
-
uint8_t
等位于sys/types.h
中 -
sin_addr
sin_port
即为以网络字节序存储的32位ip地址与端口号 - 套接字地址有非常多种,为了可以统一以指针的形式使用socket API,使用时须要转换为通用套接字地址
sockaddr
,一般进行强制类型转换就可以。
socket基本TCP API
- 相关函数定义于
sys/socket.h
中socket函数
- socket函数用以创建一个socket。
int socket(int family, int type, int protocal);
- family通常设置为
AF_INET``AF_INET6
。分别表示IPv4/6协议。 - type通常设置为
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
。分别表示字节流(TCP)。数据报(UDP),原始套接字。 - protocal表示协议族。
IPPROTO_TCP'
IPPROTO_UDP`通常设置为0也可。 - 返回值表示非负套接字描写叙述符
connect函数
- 用于建立连接
int connect(int sockfd, const struct sockaddr *servaddr, int addrlen);
- TCPclient用connect函数与server端建立连接
- 此函数将激发TCP的三次握手连接过程,直到链接建立成功或出错才返回
- 每一个socket仅仅能调用一次connect,出错后必须close当前socket再次又一次依次调用socket、connect
- 函数參数为socket描写叙述符。通用socket地址指针及其结构体大小。
bind函数
- bind将IP地址和port绑定到套接字描写叙述符
int bind(int sockfd, const struct sockadddr *myaddr, int addrlen);
- 假设sin_addr.s_addr设置为INADDR_ANY,且主机有多个网络接口。则能够在多个网络接口接受用户connect
listen函数
- listem将一个未调用connect函数的socket转换为一个被动监听套接字
int listen(int sockfd, int backlog);
- backlog规定了挂起连接的最大数量
accept函数
- 内核为任一个监听套接字维护一个正在处于握手连接阶段的未完毕连接队列,以及已完毕连接队列
- accept每次接受一个监听套接字描写叙述符,返回一个已连接队列中的已连接套接字描写叙述符
- 已连接套接字的套接字地址与地址长度存放于cliaddr与addrlen指向的内存中。
假设使用两个0来调用,则无法得到client已连接套接字表示的地址与port等信息。
- 对于每一个处理完毕的连接,应该close,否则可能耗尽套接字描写叙述符
int accept(int sockfd, struct sockaddr *cliaddr, int *addrlen)
close函数
- close一个TCP套接字默认行为是把socket标记为关闭后返回。但触发了四次挥手过程
int close(int sockfd);
传送数据
- 通常须要一个缓冲区。之后使用recv、send函数接收发送
- read、write在*nix系统上也能够
int recv(int sockfd, void *buf, size_t len, int flags); int send(int sockfd, const void *buf, size_t len, int flags);
TCP通信client与server端
client流程
- 使用connect连接server之后就能够開始数据传输
server端流程
- 须要首先绑定网络接口(bind),之后进入监听状态(listen),最后从队列中取出一个已经连接的套接字,即获得新连接(accept),之后能够開始数据传输
源码例如以下:
- server端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<stdio.h>
int main(){
struct sockaddr_in local;
int s;
int sl;
int rc;
char buf[1000];
local.sin_family = AF_INET;
local.sin_port = htons(7500);
local.sin_addr.s_addr = htonl(INADDR_ANY);
s = socket(AF_INET, SOCK_STREAM, 0);
if ( s < 0){
perror("socket call failed");
exit(1);
}
rc = bind(s, (struct sockaddr *) &local, sizeof(local));
if ( rc < 0){
perror("bind call failed");
exit(1);
}
rc = listen(s, 5);
if ( rc < 0){
perror("listen call failed");
exit(1);
}
sl = accept(s, NULL, NULL);
if ( sl < 0){
perror("accept call failed");
exit(1);
}
rc = recv(sl, buf, 10, 0);
if ( rc < 0){
perror("recv call failed");
exit(1);
}
printf("%s\n", buf);
rc = send(sl, "good", 10, 0);
if ( rc < 0){
perror("send call failed");
exit(1);
}
exit(0);
}
- client
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
int main(){
struct sockaddr_in peer;
int s;
int rc;
char buf[100];
peer.sin_family = AF_INET;
peer.sin_port = htons(7500);
peer.sin_addr.s_addr = inet_addr("127.0.0.1");
s = socket(AF_INET, SOCK_STREAM, 0);
if ( s < 0 ){
perror("socket call failed");
exit(1);
}
rc = connect(s, (struct sockaddr *) &peer, sizeof(peer));
if (rc){
perror("connect call failed");
exit(1);
}
rc = send(s, "hello", 10, 0);
if (rc <= 0)
{
perror("send call failed");
exit(1);
}
rc = recv(s, buf, 10, 0);
if (rc <= 0){
perror("recv call failed");
}
else
printf("%s\n", buf);
exit(0);
}
版权声明:本文博客原创文章,博客,未经同意,不得转载。