网络编程
在分层的网络模式中,每一层都为上一层提供一定的服务。
接口层:最下层,它包括多种逻辑链接控制和媒体访问协议。
网络层:IP协议,负责在发送端和接收端之间建立一条虚拟路径。arp,rarp
传传层:TCP/UDP协议
应用层:
为了使网络上的每台计算机通信,所有要有唯一标识还标识:物理地址(网卡地址)和IP地址
1,物理地址一个48位的位串,此地址在网卡生产过程中就固定了。
2,IP地址:TCP/IP协议能够使计算机之间进行与底层物理网络无关的通信。网络号和该网络分给计算机的主机号,分别称网络ID和主机ID
网络地址可以唯一标识网络上的每台计算机。
端口:用来区别一台计算机会同时运行多个程序。
端口号:0-65535
IP协议:负责确定路由,当到达同一目的地有多条路由时,IP协议会先一条最短的路由来奖数据分组传送到目的地计算机。
套接字地址结构
#include <linux/socket.h>
struct sockadd
{
unsigned short sa_family;//地址类型 AF_INET :tcp
char sa_data[14];//14字节协议地址
};
#include <netinet/in.h>
struct sockaddr_in
{
unsigned short sin_family;//地址类型
unsigned short int sin_port;//端口号
struct in_addr sin_addr;//IP地址
unsigned char sin_zero[8];//填充字,一般为0
};
struct in_addr
{
unsigned long s_addr;
};
struct sockaddr_in sock;
sock.sin_family = AF_INET;
sock.sin_port = htons(80);
sock.sin_addr.s_addr = inet_addr("202.205.3.195");
memset(sock.sin_zero,0,sizeof(sock.sin_zero));
memset(void *s,int c,size_t n);
创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
协议:
AF_UNIX:只在本地内通信的套接字
AF_INET:ipv4
AF_INET6:ipv6
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
int sock_fd;
sock_fd = socket(AF_INET,SOCK_STREAM,0);
//sock_fd = socket(AF_INET,SOCK_DGRAM,0);
if(sock_fd < 0)
{
perror("socket");
exit(1);
}
建立连接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
if(inet_aton("172.17.242.131",&serv_addr.sin_addr) <0)
{
perror("inet_aton");
exit(1);
}
if(connect(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
exit(1);
}
绑定套接字:用来将一个套接字和某个端口绑定在一起
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr* my_addr,socklen_t addrlen);
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock_fd,(struct sockaddr*) & serv_addr,sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
exit(1);
}
在套接字上监听
#include <sys/socket.h>
int listen(int sockfd,int backlog);
#define LISTEN_NUM 12
if(listen(sock_fd,LISTEN_NUM) < 0)
{
perror("listen");
exit(1);
}
接受连接:接受一个连接请求
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s,struct sockaddr *addr,socklen_t addrlen);
int client_fd;
int client_len;
struct sockaddr_in;
client_len = sizeof(struct sockaddr_in);
client_fd = accept(sock_fd,(struct sockaddr*)&client_addr,&client_len);
if(conn_fd < 0)
{
perror("accpet");
exit(1);
}
TCP套接字的数据传输
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd,const void *msg,size_t len,int flags);
#define BUFFERSIZE 1024
char send_buf[BUFFERSIZE];
if(send(conn_fd,send_buf,len,0) < 0)
{
perror("send");
exit(1);
}
接收数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int s,void *buf,size_t len,int flags);
char recv_buf[BUFFERSIZE];
if(recv(conn_fd,recv_buf,sizeof(recv_buf),0) < 0)
{
perror("recv");
exit(1);
}
UDP套接字的数据传输
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd,const void* msg,size_t len,int flags,
const struct sockaddr* to,socklen_t tolen);
char sendBUf[BUFFERSIZE];
struct sockaddr_in dest_addr;
memset(*dest_addr , 0, sizeof(struct sockaddr_in));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
if(inet_aton("127.0.0.1",&dest_addr.sin_addr) < 0)
{
perror("inte_aton error");
exit(1);
}
if(sendto(sock_fd,send_buf,len,0,(struct sockaddr * )&desc_addr,
sizeof(struct sockaddr_in))<0)
{
perror("sendto");
exit(1);
}
接数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,
struct sockaddr* from,socklen_t* fromlen);
char recvBuf[BUFFERSIZE];
struct sockaddr_in src_addr;
int src_len;
src_len = sizeof(struct sockaddr_in);
if(recvfrom(sockfd,recvBuf,sizeof(recv_buf),0,
(struct sockaddr*)&src_addr,&src_len) < 0)
{
perror("recvfrom error\n");
exit(1);
}
关闭套接字
#include <unistd.h>
int close(inf fd);
#include <sys/socket.h>
int shutdown(int sockfd,int how);
SHUT_RD
SHUT_WR
SHUT_RDWR
字节顺序和转换函数
大小端转换
#include <netinet/in.h>
转换成网络字节序
uint32_t htonl(uint32_t hostlong);//host to network long
uint16_t htons(uint16_t hostshort);//host to network short
反过来
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
inet函数
字符串形式地址:127.0.0.1,然而网络上需二进制且为网络字节顺序的IP地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//将字符形式IP转换成二进制网络字节序IP地址
int inet_aton(const char* cp,struct in_addr* inp);
//功能同上
in_addr_t inet_addr(const char *p);
//字符串形式的网络地址转换为主机字节顺序形式的二进制IP地址
in_addr_t inet_network(const char* cp);
//将网络字节转换成字符形式
char* inet_ntoa(struct in_addr in);
//网络号net,主机号host 组合成网络地址
struct in_addr inet_makeaddr(int net,int host);
//提取出主机地址
in_addr_t inet_lnaof(struct in_addr in);
//提取出网络地址
in_addr_t inet_netof(struct in_addr in);
修改套接字属性
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t * optlen);
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
struct timeval
{
long tv_sec;
long tv_usec;
};
------------------------------------------------------------------------------------
多路复用select
非阻塞方式套接字,服务端不断地查询各个套接字的状态,如果有数据到达则读出数据,
如果没有就查看下一个套接字,这种方法虽然简单,但在轮询过程中浪费了大量的CPU时间,效率低。
另一种:服务端进程并不主动询问套接字状态,而是向系统登记希望监视的套接字,然后阻塞
当套接字上有事件发生时,系统通知服务器进程告知哪个套接字上发生了什么事。服务端查询对应套接字并处理。
服务器进程不会去查询套接字状态,从而不会浪费CPU时间,提高了效率
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int sockfd,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout);
timeout为NULL阻塞,0非阻塞
FD_CLR(int fd,fd_set *set);//将文件描述符fd从文件描述符集合set中删除
FD_ISSET(int fd,fd_set *set);//测fd是否在set中
FD_SET(int fd,fd_set *set);
FD_ZERO(fd_set *set);//清空
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
void display_time(const char *str)
{
int seconds;
seconds = time((time_t*)NULL);
printf("%s,%d\n",string,seconds);
}
int main()
{
fd_set readfds;
struct timeval timeout;
int ret;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
while(1)
{
display_time("before select ");
ret = select(1,&readfd,NULL,NULL,&timeout);
display_time("after select");
switch(ret)
{
case 0:
printf("no data in ten seconds.\n");
exit(0);
break;
case 1:
perror("select");
exit(1);
break;
default:
getchar();
printf("data is available now.\n");
}
}
return 0;
在分层的网络模式中,每一层都为上一层提供一定的服务。
接口层:最下层,它包括多种逻辑链接控制和媒体访问协议。
网络层:IP协议,负责在发送端和接收端之间建立一条虚拟路径。arp,rarp
传传层:TCP/UDP协议
应用层:
为了使网络上的每台计算机通信,所有要有唯一标识还标识:物理地址(网卡地址)和IP地址
1,物理地址一个48位的位串,此地址在网卡生产过程中就固定了。
2,IP地址:TCP/IP协议能够使计算机之间进行与底层物理网络无关的通信。网络号和该网络分给计算机的主机号,分别称网络ID和主机ID
网络地址可以唯一标识网络上的每台计算机。
端口:用来区别一台计算机会同时运行多个程序。
端口号:0-65535
IP协议:负责确定路由,当到达同一目的地有多条路由时,IP协议会先一条最短的路由来奖数据分组传送到目的地计算机。
套接字地址结构
#include <linux/socket.h>
struct sockadd
{
unsigned short sa_family;//地址类型 AF_INET :tcp
char sa_data[14];//14字节协议地址
};
#include <netinet/in.h>
struct sockaddr_in
{
unsigned short sin_family;//地址类型
unsigned short int sin_port;//端口号
struct in_addr sin_addr;//IP地址
unsigned char sin_zero[8];//填充字,一般为0
};
struct in_addr
{
unsigned long s_addr;
};
struct sockaddr_in sock;
sock.sin_family = AF_INET;
sock.sin_port = htons(80);
sock.sin_addr.s_addr = inet_addr("202.205.3.195");
memset(sock.sin_zero,0,sizeof(sock.sin_zero));
memset(void *s,int c,size_t n);
创建套接字
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain,int type,int protocol);
协议:
AF_UNIX:只在本地内通信的套接字
AF_INET:ipv4
AF_INET6:ipv6
SOCK_STREAM
SOCK_DGRAM
SOCK_RAW
int sock_fd;
sock_fd = socket(AF_INET,SOCK_STREAM,0);
//sock_fd = socket(AF_INET,SOCK_DGRAM,0);
if(sock_fd < 0)
{
perror("socket");
exit(1);
}
建立连接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen);
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
if(inet_aton("172.17.242.131",&serv_addr.sin_addr) <0)
{
perror("inet_aton");
exit(1);
}
if(connect(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in)) < 0)
{
perror("connect");
exit(1);
}
绑定套接字:用来将一个套接字和某个端口绑定在一起
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,struct sockaddr* my_addr,socklen_t addrlen);
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(80);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sock_fd,(struct sockaddr*) & serv_addr,sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
exit(1);
}
在套接字上监听
#include <sys/socket.h>
int listen(int sockfd,int backlog);
#define LISTEN_NUM 12
if(listen(sock_fd,LISTEN_NUM) < 0)
{
perror("listen");
exit(1);
}
接受连接:接受一个连接请求
#include <sys/types.h>
#include <sys/socket.h>
int accept(int s,struct sockaddr *addr,socklen_t addrlen);
int client_fd;
int client_len;
struct sockaddr_in;
client_len = sizeof(struct sockaddr_in);
client_fd = accept(sock_fd,(struct sockaddr*)&client_addr,&client_len);
if(conn_fd < 0)
{
perror("accpet");
exit(1);
}
TCP套接字的数据传输
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd,const void *msg,size_t len,int flags);
#define BUFFERSIZE 1024
char send_buf[BUFFERSIZE];
if(send(conn_fd,send_buf,len,0) < 0)
{
perror("send");
exit(1);
}
接收数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int s,void *buf,size_t len,int flags);
char recv_buf[BUFFERSIZE];
if(recv(conn_fd,recv_buf,sizeof(recv_buf),0) < 0)
{
perror("recv");
exit(1);
}
UDP套接字的数据传输
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd,const void* msg,size_t len,int flags,
const struct sockaddr* to,socklen_t tolen);
char sendBUf[BUFFERSIZE];
struct sockaddr_in dest_addr;
memset(*dest_addr , 0, sizeof(struct sockaddr_in));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
if(inet_aton("127.0.0.1",&dest_addr.sin_addr) < 0)
{
perror("inte_aton error");
exit(1);
}
if(sendto(sock_fd,send_buf,len,0,(struct sockaddr * )&desc_addr,
sizeof(struct sockaddr_in))<0)
{
perror("sendto");
exit(1);
}
接数据
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,
struct sockaddr* from,socklen_t* fromlen);
char recvBuf[BUFFERSIZE];
struct sockaddr_in src_addr;
int src_len;
src_len = sizeof(struct sockaddr_in);
if(recvfrom(sockfd,recvBuf,sizeof(recv_buf),0,
(struct sockaddr*)&src_addr,&src_len) < 0)
{
perror("recvfrom error\n");
exit(1);
}
关闭套接字
#include <unistd.h>
int close(inf fd);
#include <sys/socket.h>
int shutdown(int sockfd,int how);
SHUT_RD
SHUT_WR
SHUT_RDWR
字节顺序和转换函数
大小端转换
#include <netinet/in.h>
转换成网络字节序
uint32_t htonl(uint32_t hostlong);//host to network long
uint16_t htons(uint16_t hostshort);//host to network short
反过来
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
inet函数
字符串形式地址:127.0.0.1,然而网络上需二进制且为网络字节顺序的IP地址
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//将字符形式IP转换成二进制网络字节序IP地址
int inet_aton(const char* cp,struct in_addr* inp);
//功能同上
in_addr_t inet_addr(const char *p);
//字符串形式的网络地址转换为主机字节顺序形式的二进制IP地址
in_addr_t inet_network(const char* cp);
//将网络字节转换成字符形式
char* inet_ntoa(struct in_addr in);
//网络号net,主机号host 组合成网络地址
struct in_addr inet_makeaddr(int net,int host);
//提取出主机地址
in_addr_t inet_lnaof(struct in_addr in);
//提取出网络地址
in_addr_t inet_netof(struct in_addr in);
修改套接字属性
#include <sys/types.h>
#include <sys/socket.h>
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_t * optlen);
int setsockopt(int sockfd,int level,int optname,const void *optval,socklen_t optlen);
struct timeval
{
long tv_sec;
long tv_usec;
};
------------------------------------------------------------------------------------
多路复用select
非阻塞方式套接字,服务端不断地查询各个套接字的状态,如果有数据到达则读出数据,
如果没有就查看下一个套接字,这种方法虽然简单,但在轮询过程中浪费了大量的CPU时间,效率低。
另一种:服务端进程并不主动询问套接字状态,而是向系统登记希望监视的套接字,然后阻塞
当套接字上有事件发生时,系统通知服务器进程告知哪个套接字上发生了什么事。服务端查询对应套接字并处理。
服务器进程不会去查询套接字状态,从而不会浪费CPU时间,提高了效率
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int sockfd,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval* timeout);
timeout为NULL阻塞,0非阻塞
FD_CLR(int fd,fd_set *set);//将文件描述符fd从文件描述符集合set中删除
FD_ISSET(int fd,fd_set *set);//测fd是否在set中
FD_SET(int fd,fd_set *set);
FD_ZERO(fd_set *set);//清空
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
void display_time(const char *str)
{
int seconds;
seconds = time((time_t*)NULL);
printf("%s,%d\n",string,seconds);
}
int main()
{
fd_set readfds;
struct timeval timeout;
int ret;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
while(1)
{
display_time("before select ");
ret = select(1,&readfd,NULL,NULL,&timeout);
display_time("after select");
switch(ret)
{
case 0:
printf("no data in ten seconds.\n");
exit(0);
break;
case 1:
perror("select");
exit(1);
break;
default:
getchar();
printf("data is available now.\n");
}
}
return 0;
}