回忆昨天内容
一、system v ipc
共享内存
获取一个键值
使用键值获取一个共享内存段的id
将共享内存段关联到进程的虚拟地址(内存)
不再使用这个共享内存段的时候,解除关联
二、网络的基础
TCP/IP协议簇
四层或者五层
应用层 传输层 网络层 链路层 物理层
ip地址 mac地址
有两部分构成 网络号 主机号
子网掩码
ifconfig ping route arp
网帧的结构 ip报文 tcp包
局域网内数据的传输
跨网段数据的传输
三、基于TCP的网络模型
三次握手 四次分手
今天内容:
一、基于tcp的编程
模型:
服务器端:
1 创建一个通讯设备,返回该设备的文件描述符
2 将sfd绑定到服务器的地址和端口号
3 将sfd设置为被动连接状态 等待客户端连接的到来,有连接到来,将连接放入未决连接队列中。
如果未决连接队列满,通知客户端。
while(1)
{
4 从未决队列中,取出一个连接 进行处理
返回一个真正的和客户端的连接
(处理,就是跟客户端的连接)
5 从连接里读取客户端的消息read
6 处理消息
7 处理结果 ,响应给客户端write
8 关闭连接
}
客户端:
1 创建一个socket设备,返回该设备的文件描述符(fd)
2 在fd上向服务器发连接
3 (连接成功)向服务器发送消息
4 等待服务器的响应信息
5 处理服务器的响应信息
6关闭和服务器端的连接
实现:
socket(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建一个通讯端点
参数:
domain
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7)
type
SOCK_STREAM 有序的 可靠的 基于连接的字节流 双向的 TCP
SOCK_DGRAM 面向数据包的 不可靠 连接可有可无 UDP
protocol 0
返回值:成功 返回新的文件描述符、
失败 -1 errno被设置
bind(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:给socket设备绑定一个名字(name? 地址家族)
参数:
sockfd 指定socket设备
addr 指定具体的地址
addrlen addr的空间的大小
返回值:成功 0
错误 -1 errno被设置
struct sockaddr?
struct sockaddr {//通用地址家族
sa_family_t sa_family;
char sa_data[14];
}
ipv4地址家族 ipv6地址家族
#include <netinet/in.h>
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};
ip地址和端口号
in_port_t 无符号的短整型 2字节
uin32_t 无符号的整型 4字节
网络字节序 大端
主机字节序<-->网络字节序
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h host
n net
l long
s short
to
ip地址 192.168.1.24
字符串 <--> 无符号整型
text <--> binary
inet_pton(3)
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
功能:将ipv4和ipv6地址从text转换为binary
参数: af 指定地址家族
AF_INET 指定ipv4
AF_INET6 指定ipv6
src ip地址字符串格式
dst 存储转换后的结果 struct in_addr
返回值:成功返回1
地址家族不存在 -1 errno被设置
0 ip地址不合法
binary-->text
inet_ntop(3)
#include <arpa/inet.h>
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);
功能:
、 参数:
af 指定地址家族
src 地址的结构体 struct in_addr
dst 指定了存储空间 转换后的字符串存放到dst指向的空间里
size 指定了dst有效空间的大小
返回值:成功返回指向dst的非空指针
失败返回 null errno被设置
man 7 ip
listen(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能: 监听客户端连接的到来 将其放入未决连接队列
参数:
sockfd 指定了一个socket 将这个socket设置为被动模式
backlog 指定了未决连接队列的最大数
返回值:成功 返回0
错误 -1 errno被设置
accept(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:受理一个连接
参数:
sockfd 指定socket 这是监听客户端连接的socket 从这个socket的未决连接队列里取出第一个未决连接请求,
进行处理。返回一个连接描述符
addr 将客户端的地址存储到addr指向的地址空间里。
addrlen addr结构体的尺寸
返回值: 错误-1 errno被设置
成功 返回一个新的描述符 和客户端的连接
举例说明 编写基于tcp的服务器端
代码参见 server.c
connect(2)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:在socket上发起一个连接
参数:sockfd 指定socket 在这个socket向addr发起连接
addr 指定了地址空间,向这个地址空间发起连接
addrlen addr的尺寸
返回值:成功返回0
错误 -1 errno被设置
将字符串从小写转换为大写 toupper(3) #include<ctype.h>
server.c
#include<t_stdio.h>
#include<t_file.h>
#include<sys/socket.h>
#include<ctype.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main(){
// struct sockaddr serv;//通用地址
struct sockaddr_in serv,clie;//具体ipv4地址
char* buf;
int r;
socklen_t len;
char ip[32];
//创建socket 返回该设备的文件描述符
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1) E_MSG("socket",-1);
//初始化服务器的ip地址和端口
serv.sin_family=AF_INET;
serv.sin_port=htons(3333);
serv.sin_addr.s_addr=htonl(INADDR_ANY);//本地所有端口的ip地址
//将sfd绑定到本地地址
int b=bind(sfd,(struct sockaddr*)&serv,sizeof(serv));
if(b==-1) E_MSG("bind",-1);
//将sfd设置为被动连接 监听客户端的连接
//连接到来的时候,将连接放入未决连接队列中
int l=listen(sfd,5);
if(l==-1) E_MSG("listen",-1);
while(1){//受理连接
len=sizeof(clie);
int cfd=accept(sfd,(struct sockaddr *)&clie,&len);
if(cfd==-1) E_MSG("accept",-1);
//客户端ip地址和端口保存到clie结构体中了
//binary-->text
printf("%s\n",inet_ntop(AF_INET,&clie.sin_addr,ip,32));
//从连接描述符中读取客户端发送来的数据到buf中
r=read(cfd,buf,128);
//处理客户端的数据
for(int i=0;i<r;i++)
buf[i]=toupper(buf[i]);
// buf="Stupid JerryFeng\n";
//响应客户端
write(cfd,buf,r);
//关闭
close(cfd);
}
return 0;
}
client.c
#include<t_stdio.h>
#include<t_file.h>
#include<t_neth>
#include<string.h>
int main(int argc,char* argv[]){
struct SA serv;
char *msg="this is a test...\n";
char buf[128];
//创建socket 返回该设备的文件描述符
int sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd==-1) E_MSG("socket",-1);
//初始化服务器的ip地址和端口号
serv.sin_family=AF_INET;
serv.sin_port=htons(3333);
//知道服务器的ip地址 127.0.0.1
inet_pton(AF_INET,argv[1],&serv.sin_addr);
//向服务器发起连接
int c=connect(sfd,(SA *)&serv,sizeof(serv));//触发三次握手
if(c==-1) E_MSG("connect",-1);
//如果连接服务器成功
//向服务器发送消息
while(1){
// scanf("%s",buf);
write(sfd,msg,strlen(msg));//没有字符串最后的'\0'
//等待服务器的响应消息
int r=read(sfd,buf,128);
write(1,buf,r);
}
//关闭本次连接
close(sfd);
return 0;
}