Welcome 微信登录

首页 / 操作系统 / Linux / Linux IPC tcp/ip socket 编程

模型

#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>//服务器:socket()//创建socketstruct sockaddr_in//准备通信地址bind()//绑定socket和addrlisten()//创建listening socketaccept()//创建connect socketsend()/recv() //进行通信close() //关闭socket//客户端:socket()//创建socket准备通信地址:服务器的地址connect() //链接socket和通信地址send()/recv() //进行通信close() //关闭socket

socket()

//创建网络端点,返回socket文件描述符,失败返回-1设errnoint socket(int domain, int type, int protocol);domain :协议族(protocol family)(网络通讯(IP)还是本地通讯(xxx.socket))
  • AF_INET用于实现给予ipv4网络协议的网络协议
type :协议(TCP还是UDP)
  • SOCK_STREAM //流式套接字, 实现包括但不限于TCP协议,which is有序,可靠双向面向连接的字节流通信方式
protocol: 特殊协议, 一般给0

准备通信地址:

struct sockaddr{//主要用于函数的形参类型, 很少定义结构体变量使用, 叫做通用的通信地址类型//$man bindsa_family_t sa_family;charsa_data[14];}struct sockaddr_in{ //准备网络通信的通信地址 //$man in.hsa_family_t sin_family; //协议族, 就是socket()的domain的AF_INETin_port_t sin_port; //端口号struct in_addrsin_addr; //IP地址}struct in_addr{ in_addr_t s_addr; //整数类型的IP地址}

bind():

//把通信地址和socket文件描述符绑定,用在服务器端,成功返回0,失败返回-1设errnoint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd: socket文件的fd(returned by socket())
addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
addrlen: 通信地址的大小, 使用sizeof();

listen()

//创建侦听socket,把sockfd标记的socket标记为被动socket,被动socket表示这个socket只能用来接收即将到来的连接请求,不再用于读写操作和通信,接收连接请求的是accept()//成功返回0,失败返回-1设errnoint listen(int sockfd, int backlog);backlog:排队等待“被响应”连接请求队列的最大长度 eg: 接待室的最大长度

accept()

//创建连接socket,返回连接socket的文件描述符,成功返回文件描述符,失败返回-1设errnoint accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);addr : 结构体指针, 用于带出客户端的通信地址
addlen : 结构体指针, 用于带出通信地址的大小
ATTENTION: listen()把socket()创建的sockfd变为listening socket, 负责侦听哪个client连接上了(即不但要知道连上没, 还要知道谁连上了, 这个SOCK_STREAM的socket有这个能力), accept()提取排队中的最上面的一个client, 给它一个conneted socket, 这样这个client就可以和server沟通了, 就是说这里有两个socket, 一个负责侦听一个负责通信

send()

//向指定的socket发送指定的数据,成功返回实际发送数据的大小,失败返回-1设errnossize_t send(int sockfd, const void *buf, size_t len, int flags);sockfd: 用于通信的socket描述符(returned by accept())
buf : 被发送数据的缓冲区首地址
len : 被发送数据的大小
flags: 发送的标志, 如果给0等同于write()

recv()

//从指定的socket接收数据,成功返回接收的数据的大小,失败返回-1设errnossize_t recv(int sockfd, void *buf, size_t len, int flags);sockfd: 用于通信的socket描述符(returned by accept())
buf: 接收数据的缓冲区首地址
len: 接收数据的大小
flags: 发送的标志, 如果给0等同于read()

connect():

//初始化一个socket的连接,用在客户端,成功返回0,失败返回-1设errnoint bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);sockfd: socket文件的fd(returned by socket())
addr: 需要强制类型转换成socketaddr_un或soketaddr_in, 参见上
addrlen: 通信地址的大小, 使用sizeof();

例子-多进程并发tcp/ip协议服务器模型

除了这种多进程的并发模型,还有多线程并发和I/O多路复用并发等方式//创建server, 用多进程同时响应多个client的请求, 当client发来 “bye”的时候断开连接, 按下Ctrl+C关闭服务器#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>//省略了几个头文件int sockfd; //全局变量void fa(int signo){printf("closing server... ");sleep(3);int res=close(sockfd);if(-1==res)perror("close"),exit(-1);printf("server closed ");exit(0);}int main(){… //同上一个程序//set SIGINTprintf("Press ctrl+c to close server ");if(SIG_ERR==signal(SIGINT,fa))//整个程序,包括第一个while(1)是通过信号处理终止的perror("signal"),exit(-1); while(1){ //只要有client接入就创建新进程与之通信struct sockaddr_in recv_addr;socklen_t len=sizeof(recv_addr);int CnnSockfd=accept(sockfd,(struct sockaddr*)&recv_addr,&len); //如果侦听队列里面有client就accept(), 否则就在这阻塞着,不继续执行,除非遇到Ctrl+C终止整个进程if(-1==CnnSockfd)perror("accept"),exit(-1);char *ip=inet_ntoa(recv_addr.sin_addr);printf("client:%s linked ",ip);pid_t pid=fork();if(-1==pid)perror("fork()"),exit(-1);if(0==pid){if(SIG_ERR==signal(SIGINT,SIG_DFL))perror("signal"),exit(-1);//每个child处理一个client,所以已经不需要listening socket了,可以把它关了//如果不关子进程也会有一个listenfd,会和父进程一起抢,不应该res=close(sockfd);if(-1==res)perror("close"),exit(-1);while(1){ //只要client发数据就处理,除非遇到 “bye”char buf[100]={0};res=recv(CnnSockfd,buf,sizeof(buf),0);if(-1==res)perror("recv"),exit(-1);printf("client%s,data sent:%s ",ip,buf);if(!strcmp(buf,"bye")){ //遇到“bye”就不再待命,break掉准备断开连接printf("client%s has been unlinked ",ip);break;}res=send(CnnSockfd,"I received!",12,0);if(-1==res)perror("send"),exit(-1);}res=close(CnnSockfd); //断开连接即close(相应的connected socket)if(-1== res)perror("close"),exit(-1);exit(0);//断开了连接了,就可以exit子进程了}res=close(CnnSockfd); //if(-1==res)perror("close"),exit(-1);}return 0;}本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-11/136762.htm