网络编程(Linux)(C)

13 篇文章 0 订阅
1 篇文章 0 订阅

进程间通信(套接字)

TCP/IP协议:

TCP:纠错
IP:连接
协议:按一定的规则完成某件事

OSI七层网络模型(理想模型):

应用层:应用程序(协议)
表示层:数据处理,加密/解密
会话层:进程逻辑关系和物理关系做联系
传输层:保证数据的传输(纠错)
网络层:数据分组,路由(选择线路)
数据链路:把数据组成成可以发送的帧格式
物理层:信号转换

TCP/IP四层模型:

应用层:tftp、email
传输层:tcp、udp
网络层:ip、icmp
网络接口和物理层:网卡驱动、物理接口

预备知识:

ip地址:标识主机,32位数字(ipv4)点分式 192.168.1.1 0.0.0.0 - 255.255.255.255(ipv6)
	特殊地址:127.0.0.1本地回环测试地址,0.0.0.0本机地址,最后一位为255广播地址
	A:(前一位为网络号,后三位为主机号,最高位为0)0.0.0.0-127.255.255.255
	B:(前两位为网络号,后两位为主机号,最高位为10)128.0.0.0-191.25.255.255
	C:(前三位为网络号,后一位为主机号,最高位为110)192.0.0.0-223.255.255.255
	D:组播地址(最高位1110)224.0.0.0-239.255.255.255
	E:保留地址
端口号:标识唯一进程,2字节0-65535(0-5000:操作系统)
字节序:
	大端序:高字节存储在低字节
	小端序:低字节存储在低字节
网络字节序:
	大端序
传输层协议:
	tcp:稳定,有连接,数据流式,无失序
	udp:不稳定,无连接,数据报式,有失序
套接字类型:(本质为文件)
	流式套接字:
	报式套接字:
	unix域套接字(本机进程通信)

tcp:

	cs构架:客户端与服务器
	服务器:
		创建套接字
		#include <sys/types.h>
   		#include <sys/socket.h>
		int socket(int domain, int type, int protocol);
		返回值:成功返回文件描述符,失败-1
		domain:网络层协议(AF_INET ipv4)
		type:套接字类型(流,报等)
		protocol:子协议(无为0)

		绑定套接字信息(ip,端口号)
		int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
		返回值:成功0,失败-1
		sockfd:文件描述符
		addr:结构体类型指针(发送)
		addrlen:结构体大小(发送)


		struct sockaddr //(通用结构体)
		{
           				sa_family_t sa_family; //地址族(网络协议)
           				char        sa_data[14]; //协议信息
       	};
		struct sockaddr_in //专用结构体(man 7 ip)
		{
           				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 (IP地址)*/
       	};
		struct in_addr
		{
           				uint32_t       s_addr;     /* address in network byte order (地址的网络字节序)*/
       	};

		
		开启监听队列(从close状态转变为listen状态)
		int listen(int sockfd, int backlog);
		返回值:成功0,失败-1
		sockfd:套接字文件描述符
		backlog:监听队列大小(一般3-4)

		被动等待连接(取信息)
		int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
		返回值:成功接收到的套接字文件描述符,失败-1
		sockfd:套接字文件描述符
		addr:结构体类型指针(接收)
		addrlen:结构体大小(接收)(addrlen必须先初始化为sizeof(addr))
		
		功能(接收、发送)
			接收:
			ssize_t recv(int sockfd, void *buf, size_t len, int flags);
			返回值:成功实际接收的字节数,失败-1
			sockfd:套接字文件描述符
			buf:缓冲区空间
			len:预计接收的大小
			flags:方式(默认0)

			发送:
			ssize_t send(int sockfd, void *buf, size_t len, int flags);
			返回值:成功实际发送的字节数,失败-1
			sockfd:套接字文件描述符
			buf:缓冲区空间
			len:预计发送的大小
			flags:方式(默认0)

		转为网络字节序
		#include <arpa/inet.h>
   		uint32_t htons(uint32_t hostlong);//转换端口号网络字节序
		uint32_t ntohs(uint32_t netlong);//反转
		
		#include <sys/socket.h>
   		#include <netinet/in.h>
   		#include <arpa/inet.h>
		in_addr_t inet_addr(const char *cp);//将IP地址转化为网络字节序
		char *inet_ntoa(struct in_addr in);//反转

	客户端:
		创建套接字
		绑定套接字(可以省略,不绑定自己分配)
		主动连接服务器
		#include <sys/types.h>         
   		#include <sys/socket.h>
   		int connect(int sockfd, const struct sockaddr **addr,socklen_t addrlen);
		返回值:成功0,失败-1
		sockfd:套接字文件描述符
		addr:结构体类型指针(发送)
		addrlen:结构体大小(发送)

udp:

单播
	服务器:
	创建套接字
	绑定套接字
	while(1)
	{
		收发
	}

	客户端:
	创建套接字
	while(1)
	{
		收发
	}

	ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
	功能:接收消息
	sockfd:套接字文件描述符
	buf:储存的缓冲区
	len:缓冲区大小
	flags:默认0
	src_addr:接收发送方的信息
	addrlen:结构体大小
		
	ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
	功能:发送消息
	sockfd:套接字文件描述符
	buf:储存的缓冲区
	len:缓冲区大小
	flags:默认0
	src_addr:接收发送方的信息
	addrlen:结构体大小

组播
	组播地址(最高位1110)224.0.0.0-239.255.255.255

	int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
	功能:设置套接字属性
	返回值:成功返回0,失败-1
	参数:sockfd:套接字文件描述符
	level:设置的协议是哪一层的(组播:IPPROTO_IP)
	optname:具体指令(组播:IP_ADD_MEMBERSHIP)
	optval:根据不同指令传递的值
	optlen:结构体大小

广播
	广播地址:X.X.X.255

	int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
	功能:设置套接字属性
	返回值:成功返回0,失败-1
	参数:sockfd:套接字文件描述符
	level:设置的协议是哪一层的(广播:SOL_SOCKET)
	optname:具体指令(广播:SO_BROADCAST)
	optval:根据不同指令传递的值
	optlen:结构体大小

UNIX域套接字

由于进程通信
	文件描述符
	证书传递
tcp域套接字
	struct sockaddr_un 
	{
          sa_family_t sun_family;               /* AF_UNIX */
          char        sun_path[108];            /* Pathname */
    };

udp域套接字(不会出错,不会失序)

tcp握手挥手:

SYN:请求同步标志,为1的时候为有效 
ACK:应答标志,表示接受到所发的数据,1为有效 
FIN:结束请求标志,1为有效

ack:应答,值为告诉对方下一次所发数据地址 
seq:值为所发数据地址

三次握手:
	客户端发起,SYN=1 seq=x,客户端CLOSED->SYN_SEND
	服务器CLOSED->LISTEN,接收消息后,ACK=1 ack=x+1 SYN=1 seq=y,服务器LISTEN->SYN_RECIVE
	客户端接收消息ACK=1,ack=y+1,客户端SYN_SNED->ESTABLISHED,服务器接收后SYN_RECIVE->ESTABLISHED
四次挥手:
	客户端发起,FIN=1 seq=x ,客户端ESTABLISHED->FIN_WAIT_1
	服务器接收后,ACK=1 ack=x+1 seq=y,如果服务器还有没发完的内容,则全部发送,服务器ESTABLISHEED->CLOSE_WAIT
	服务器发送,FIN=1 ack=x+1 seq=z,服务器CLOSE_WAIT->LAST_ASK,客户端FIN_WAIT_1->FIN_WAIT_2
	客户端接收,ACK=1,ack=z+1,seq=x+1,发送后客户端FIN_WAIT_2->TIME WAIT等待两个最长报文时间,进入CLOSED,服务器LAST_ASK->CLOSED

并发服务器:

服务器不能同时发送和接收
	读写做成多进程/多线程
	把阻塞IO变为非阻塞IO
服务器不能同时服务多个客户端
	步骤(进线程)
	创建套接字
	绑定套接字
	监听套接字
	while(1)
	{
		被动等待
		开启进程
	}

linuxIO模型:

阻塞
非阻塞
信号驱动
IO多路复用
		select(线性结构)(数组)
			#include <sys/select.h>
			#include <sys/time.h>
			#include <sys/types.h>
 				#include <unistd.h>

			int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
			功能:将位图映射到内核空间
			nfds:文件描述符个数
			readfds:接收位图
			writefds:发送位图
			exceptfds:远程位图
			timeout:大于0超时时间,0非阻塞,NULL阻塞
			
   			void FD_CLR(int fd, fd_set *set);	//位图清空
  				int  FD_ISSET(int fd, fd_set *set);//判断是否存在资源
			void FD_SET(int fd, fd_set *set);//设置关心文件
  				void FD_ZERO(fd_set *set);//设置为不关心
			
			步骤
			服务器初始化
			创建位图,清空位图
			把sockfd放入位图
			while(1)
			{
				把位图拷贝到内核
				判断是哪个文件描述符准备好了
				如果是sockfd准备好了就accept,把接收到的cnfd加入位图
				如果是cnfd就做客户端的应答
			}
			当客户端断开链接,需要把对应文件描述符从位图中删除

			特点
			需要轮询,两次拷贝,处理的文件描述符有限
			
		poll(线性结构)(结构体数组)
			与select基本一致
			#include <poll.h>
  				int poll(struct pollfd *fds, nfds_t nfds, int timeout);
			struct pollfd 
			{
          						int   fd;         /* file descriptor */
          						short events;     /* requested events */
          						short revents;    /* returned events */
      			};
			功能:将位图映射到内核空间
			返回值:成功返回准备好的文件描述符个数,失败-1
			fds:存储文件描述符的结构体的地址
			nfds:元素个数
			timeout:大于0超时时间,0非阻塞,-1阻塞
			
		

		epoll(索引结构)(红黑树查找,返回结构体数组)
			文件描述符不受限制,不需要轮询,只要一次拷贝
			文件描述符个数不受限制,不需要轮询,只需要一次拷贝
			#include <sys/epoll.h>

			int epoll_create(int size);
			功能 :创建一个 epoll 对象,返回该对象的描述符
			size:从 Linux 内核 2.6.8 版本起,size 这个参数就被忽略了,只要求size 大于 0 即可

			int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
			功能 :操作控制 epoll 对象,主要涉及 epoll 红黑树上节点的一些操作,比如添加节点,删除节点,修改节点事件
			epfd:通过 epoll_create 创建的 epoll 对象句柄。
			op:对红黑树的操作,添加节点、删除节点、修改节点监听的事件,分别对应EPOLL_CTL_ADD,EPOLL_CTL_DEL,EPOLL_CTL_MOD
			fd:需要添加监听的 socket 描述符,可以是监听套接字,也可以是与客户端通讯的通讯套接字
			event:添加事件信息
			typedef union epoll_data 
			{
           					void        *ptr;
           					int          fd;
           					uint32_t     u32;
           					uint64_t     u64;
      		} epoll_data_t;
       		struct epoll_event 
			{
           					uint32_t     events;      /* Epoll events */
           					epoll_data_t data;        /* User data variable */
       		};

			int epoll_wait(int epid, struct epoll_event *events, int maxevents, int timeout);
			功能 :阻塞一段时间并等待事件发生,返回发生的事件集合
			epid:epoll_create 返回的 epoll 对象描述符。
			events:存放就绪的事件集合,这个是传出参数。
			maxevents:代表可以存放的事件个数,也就是 events 数组的大小。
			timeout:>0阻塞时间长短,-1 阻塞,0立即返回
			返回值:>0返回发生活动的文件描述符数量,=0等待,<0出错
			
异步IO(windows特有)

超时检测:

IO多路复用
signal:alarm
setsockopt:
	int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen)
	功能:设置套接字属性
	返回值:成功返回0,失败-1
	sockfd:套接字文件描述符
	level:设置的协议是哪一层的
	optname:具体指令
	optval:根据不同指令传递的值
	optlen:结构体大小

部分代码示例

//tcp_server.c服务器
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{ 
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(-1==sockfd)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);//转换字节序函数
    addr.sin_addr.s_addr = inet_addr("192.168.10.200");//127.0.0.1本机回环地址
    int opt=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    if(-1==bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)))
    {
        perror("bind");
        return -1;
    }
    if(-1==listen(sockfd,3))
    {
        perror("listen");
        return -1;
    }
    printf("Wait connect\n");
    struct sockaddr_in addr_client;
    socklen_t len = sizeof(addr_client);
    int connfd = accept(sockfd,(struct sockaddr*)&addr_client,&len);
    if(-1==connfd)
    {
        perror("accept");
        return -1;
    }
    printf("Connected\n");
    printf("ip=%s port=%d\n",inet_ntoa(addr_client.sin_addr),ntohs(addr_client.sin_port));
    char buf[4096]={0};
    int total=0;
    int fd = open("./picture/1.png",O_RDWR | O_CREAT,0666);
    if(fd<0)
    {
        perror("open");
        return -1;
    }




    while(1)
    {
        int ret = recv(connfd,buf,sizeof(buf),0);
        int retr = write(fd,buf,ret);
        total+=retr;
        printf("Recive %d byte\n",retr);
        int rets = send(connfd,buf,retr,0);
        printf("Send %d byte\n",rets);
        if(ret<=0)
        {
            printf("Trans over\n");
            printf("total %d byte\n",total);
            break;
        }
        puts(buf);
//        sleep(1);
        //puts(buf);
        //memset(buf,0,0);
        //fgets(buf,sizeof(buf),stdin);
        //send(connfd,buf,sizeof(buf),0);

        memset(buf,0,0);
    }
    return 0;
} 
//tcp_client.c客户端
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
int main(int argc, char *argv[])
{ 
    int sockid = socket(AF_INET,SOCK_STREAM,0);
    if(sockid<0)
    {
        perror("sockid");
        return -1;
    }
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
    addr.sin_addr.s_addr = inet_addr("192.168.10.200");
    int retc = connect(sockid,(const struct sockaddr*)&addr,sizeof(addr));
    if(retc < 0)
    {
        perror("connect");
        return -1;
    }
    else
    {
        printf("Connected\n");
    }
    while(1)
    {
        char buf[64];
        printf("Input:\n");
        fgets(buf,sizeof(buf),stdin);
        send(sockid,buf,sizeof(buf),0);
    }
    return 0;
} 

//udp广播发送
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
    addr.sin_addr.s_addr = inet_addr("192.168.10.255");

    int opval = 1;
    int rets = setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opval,sizeof(opval));
    if(rets<0)
    {
        perror("setsockopt");
        return -1;
    }

    while(1)
    {
        char buf[1024]={0};
        fgets(buf,sizeof(buf),stdin);
        int retst = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,sizeof(addr));
    }
    return 0;
}
//udp广播接收
#include <stdio.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char *argv[])
{ 
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
    addr.sin_addr.s_addr = inet_addr("0");
    int retb = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
    if(retb<0)
    {
        perror("bind");
        return -1;
    }

    struct sockaddr_in recaddr;
    int len  = sizeof(recaddr);
    while(1)
    {
        char buf[1024]={0};
        int retr = recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&recaddr,&len);
        printf("ip:%s say:%s\n",inet_ntoa(recaddr.sin_addr),buf);
    }
    return 0;
} 
//udp组播发送
#include <stdio.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7777);
    addr.sin_addr.s_addr = inet_addr("230.0.0.6");
    while(1)
    {
        char buf[1024]={0};
        fgets(buf,sizeof(buf),stdin);
        int rets = sendto(sockfd,buf,sizeof(buf),0,(struct sockaddr*)&addr,sizeof(addr));
        if(rets<0)
        {
            perror("rets");
            return -1;
        }
    }
    return 0;
} 

//udp组播接收
#include <stdio.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

int main(int argc, char *argv[])
{ 
    int sockfd = socket(AF_INET,SOCK_DGRAM,0);
    if(sockfd<0)
    {
        perror("socket");
        return -1;
    }

    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("230.0.0.125");
    mreq.imr_interface.s_addr = inet_addr("0");

    int rets = setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
    if(rets<0)
    {
        perror("setsockopt");
        return -1;
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(6666);
    addr.sin_addr.s_addr = inet_addr("0");

    int retb = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));
    if(retb<0)
    {
        perror("bind");
        return -1;
    }

    while(1)
    {
        char buf[1024];
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        puts(buf);
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值