一、套接字地址结构
进行套接字编程需要指定套接字的地址作为参数,不同的协议族有不同的地址结构定义方式。
通常使用sockaddr开头,每个协议族有一个唯一的后缀。
1、通用的套接字地址结构
-
struct sockaddr{ //套接字地址结构
-
sa_family_t sa_family; //协议族
-
char sa_data[14]; //协议族数据
-
};
2、以太网套接字地址结构
-
struct sockaddr_in{ //以太网套接字地址结构
-
u8 sin_len; //结构struct sockaddr_in的长度,16
-
u8 sin_family; //通常为AF_INET
-
u16 sin_port; //16位的端口号,网络字节序
-
struct in_addr sin_addr;//IP地址32位
-
char sin_zero[8]; //未用
-
};
-
struct in_addr{ //IP地址结构
-
u32 s_addr; //32位IP地址,网络字节序
-
};
二、TCP网络编程流程
TCP网络编程有两种模式,分别是服务器模式和客户端模式。
服务器模式:创建一个服务程序,等待客户端用户的连接,接收用户请求,根据用户的请求进行处理;
客户端模式:根据服务器的地址和端口进行连接,向服务器发送请求并对服务器的相应进行数据处理。
1、服务器端设计模式
1)套接字初始化socket()
2)套接字与端口的绑定bind()
3)设置服务器的监听listen()
4)接收客户端连接accept()
5)接收和发送数据read()\write()
6)套接字关闭close()
2、客户端设计模式
1)套接字初始化socket()
2)连接服务器connect()
3)读写网络数据read()\write()
4)套接字关闭close()
三、套接字接口函数
1、socket()
socket()函数创建一个套接字文件描述符
-
函数原型:
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
int socket(int domain,int type,int protocol);
-
参数说明:
-
domain:网络通信域
-
PF_UNIX,PF_LOCAL 本地通信
-
PF_INET ipv4协议
-
PF_INET6 ipv6协议
-
PF_IPX IPX协议
-
PF_NETLINK 内核用户界面设备
-
PF_X25 x.25协议
-
PF_AX25 ax.25协议
-
PF_ATMPVC 原始ATM PVC访问
-
PF_APPLETALK appletalk协议
-
PF_PACKET 底层包访问
-
type:套接字通信类型
-
SOCK_STREAM:TCP连接
-
SOCK_DGRAM:UDP连接
-
SOCK_SEQPACKET:序列化包
-
SOCK_RAW:原始网络协议访问
-
SOCK_RDM:提供可靠的数据报文,可能数据会有乱序
-
SOCK_PACKET:专用类型
-
protocol:指定协议的特定类型
引用层函数socket()与内核函数的关系:
用户调用函数sock=socket(AF_INET,SOCK_stream,0),这个函数会调用系统调用函数sys_socket(AF_INET,SOCK_STREAM,0).
系统调用sys_socket()分为两个部分,一部分调用sock_create()函数生成内核socket结构,另一部分调用sock_map_fd()
函数与文件描述符绑定,将绑定的文件描述符值传给应用层。
内核sock结构
-
struct socket{
-
socket_state state;//socket状态
-
unsigned long flags;//socket标志
-
const struct proto_ops *ops;//协议特定的socket操作
-
struct fasync_struct *fasync_list;//异步唤醒列表
-
struct file *file;//文件指针
-
struct sock *sk;//内部网络协议结构
-
wait_queue_head_t wait;//多用户时的等待队列
-
short type;//socket类型
-
}
2、bind()
对套接字文件描述符进行地址和端口的绑定
-
函数原型:
-
#include<sys/types.h>
-
#include<sys_socket.h>
-
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
-
参数说明:
-
sockfd:套接字文件描述符
-
my_addr:地址结构指针
-
addrlen:地址结构长度
应用层bind()函数和内核函数的关系
应用层函数bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))调用系统调用
sys_bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))。sys_bind()函数首先调用函数
sockfd_lookup_light()来获得文件描述符sockfd对应的内核struct sock结构,然后调用函数move_addr_to_kernel()
将应用层的参数my_addr复制进内核,放到address变量中。
3、listen()
初始化服务器可连接队列
-
#include<sys/socket.h>
-
int listen(int sockfd,int backlog);
-
参数说明:
-
sockfd:套接字文件描述符
-
backlog:等待队列中的客户端的长度
应用层listen()和内核层listen()的关系:
应用层的listen()函数对应于系统调用sys_listen()函数。sys_listen()函数首先调用sockfd_lookup_light()
获得sockfd对应的内核结构struct sock,得到backlog值。然后调用抽象的listen()函数(如AF_INET的listen()函数)
inet_listen()。inet_listen()函数首先判断是否合法的协议族和协议类型,然后更新socket的状态值为TCP_LISTEN,
然后为客户端的等待队列申请空间并设定监听端口。
4、accept()
接收客户端的请求。调用accept成功后会返回一个新的套接字文件描述符来表示客户端的连接。
-
函数原型:
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
-
参数说明:
-
sockfd:socket()函数创建的套接字文件描述符
-
addr:客户端连接的地址结构
-
addrlen:客户端地址结构长度
应用层accept()函数和内核函数的关系
应用层的accept()函数对应内核层的sys_accept()系统调用函数。函数sys_accept()查找文件描述符对应的内核socket
结构、申请一个用于保存客户端连接的新内核socket结构、执行内核接收函数、获得客户端的地址信息、将连接的客户
端地址信息复制到应用层的用户、返回连接客户端socket对应的文件描述符。
5、connect()
连接指定参数的服务器。
-
函数原型:
-
#include<sys/types.h>
-
#include<sys.socket.h>
-
int connect(int sockfd,struct sockaddr *,int addrlen);
-
参数说明:
-
sockfd:套接字文件描述符
-
struct sockaddr:服务器地址结构指针
-
addrlen:服务器地址结构长度
6、write()/read()
套接字的读写方式和普通文件的读写方式相同
int write(int sockfd,char *buf,int len);
int read(int sockfd,char *buf,int len);
7、close()
关闭套接字,释放资源
int close(int sockfd);
8、shutdown()
提供多种方式关闭连接,关闭双连接的一部分
-
#include<sys/socket.h>
-
int shutdown(int s,int how);
-
参数说明:
-
s:套接字文件描述符
-
how:切断方式
-
SHUT_RD:切断读
-
SHUT_WD:切断写
-
SHUT_RDWD:切断读写
四、示例
1、服务器端程序server_tcp.c
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<strings.h>
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
#include<unistd.h>
-
#include<linux/in.h>
-
-
#define PORT 8888 //端口
-
#define BACKLOG 5 //监听队列的长度
-
-
//服务器对客户端的处理
-
void process_conn_server(int s)
-
{
-
ssize_t size=0;
-
char buffer[1024];
-
-
for(;;)
-
{
-
size=read(s,buffer,sizeof(buffer));
-
if(size==0)
-
{
-
printf("read error\n");
-
return ;
-
}
-
sprintf(buffer,"%s bytes",size);
-
write(s,buffer,sizeof(buffer)+1);
-
}
-
close(s);
-
}
-
-
int main(int argc,char *argv[])
-
{
-
int ss,cc;
-
struct sockaddr_in server_addr,client_addr;
-
int err;
-
pid_t pid;
-
-
//创建流式套接字TCP
-
ss=socket(AF_INET,SOCK_STREAM,0);
-
if(ss<0)
-
{
-
printf("creat sock error\n");
-
return -1;
-
}
-
-
//设置服务器地址
-
bzero(&server_addr,sizeof(server_addr));
-
server_addr.sin_family=AF_INET;
-
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
-
server_addr.sin_port=htons(PORT);
-
-
//绑定地址到套接字
-
err=bind(ss,(struct sockaddr *)&server_addr,sizeof(server_addr));
-
if(err<0)
-
{
-
printf("bind error\n");
-
return -1;
-
}
-
-
//设置监听队列
-
err=listen(ss,BACKLOG);
-
if(err<0)
-
{
-
printf("listen error\n");
-
return -1;
-
}
-
-
//主循环过程
-
for(;;)
-
{
-
int addrlen=sizeof(struct sockaddr);
-
//接收客户端连接
-
cc=accept(ss,(struct sockaddr *)&client_addr,&addrlen);
-
if(cc<0)
-
{
-
continue;
-
}
-
-
//创建子进程
-
pid=fork();
-
if(pid==0)//子进程
-
{
-
close(ss);//关闭继承的父进程的服务器端套接字文件描述符
-
process_conn_server(cc);
-
}else{
-
close(cc);//父进程中关闭子进程的连接
-
}
-
}
-
close(ss);
-
-
return 0;
-
}
2、客户端网络程序client_tcp.c
-
#include<stdio.h>
-
#include<stdlib.h>
-
#include<strings.h>
-
#include<sys/types.h>
-
#include<sys/socket.h>
-
#include<unistd.h>
-
#include<linux/in.h>
-
-
#define PORT 8888 //端口
-
-
void process_conn_client(int ss)
-
{
-
ssize_t size=0;
-
char buffer[1024];
-
-
for(;;)
-
{
-
size=read(0,buffer,sizeof(buffer));
-
if(size==0)
-
{
-
printf("read error\n");
-
return;
-
}
-
write(ss,buffer,size);
-
size=read(ss,buffer,sizeof(buffer));
-
write(1,buffer,size);
-
}
-
}
-
-
int main(int argc,char *argv[])
-
{
-
struct sockaddr_in server_addr;
-
int err;
-
int ss;
-
-
//创建流式套接字
-
ss=socket(AF_INET,SOCK_STREAM,0);
-
if(ss<0)
-
{
-
printf("creat socket error\n");
-
return -1;
-
}
-
-
//设置服务器地址
-
bzero(&server_addr,sizeof(server_addr));
-
server_addr.sin_family=AF_INET;
-
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
-
server_addr.sin_port=htons(PORT);
-
inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
-
-
//连接服务器
-
connect(ss,(struct sockaddr *)&server_addr,sizeof(struct sockaddr));
-
process_conn_client(ss);
-
close(ss);//关闭连接
-
-
return 0;
-
}
五、信号处理
注册信号处理函数
#include
typedef void (sighandler_t)(int);
sighandler_t signal(int signum,sigheadler_t handler);
阅读(2003) | 评论(0) | 转发(0) |