linux网络编程


进程间通信都是 依赖于内核,所以缺点是无法进行多机通信

网络编程中:地址=IP地址+端口号
端口号:对于linux应用层开发用5000-10000,低于3000是操作系统用;
socket套接字用到的协议最多的是TCP和UDP,
TCP:面向连接,有点像打电话
UDP:面向报文,不关心是否连接,有点相发消息

1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前―不需要建立连接
2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
3.TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

  • 字节序:

little endian小端字节序
big endian 大端字节序
网络字节序=大端字节序
x86序列CPU都是小端字节序

字节序转换api
#include <netinet/in.h>
uint16_t htons(uint16 t host16bitvalue);//返回网络字节序的值
uint32_t htonl(uint32t host32bitvalue);//返回网络字节序的值
uint16_t ntohs(uint16 t net16bitvalue); //返回主机字节序的值uint32_t 
ntohl(uint32_t net32bitvalue); //返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),l代表long(4个字节),通过上面的4个函数可以
实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作
系统自己获取

一.int socket(int domain,int type,int protocol)–连接协议

domain:
指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP 协议族);
●AF_INET IPv4因特网域
●AF_INET6 IPv6因特网域
●AF_UNIX Unix 域
●AF_ROUTE 路由套接字
●AF_KEY 密钥套接字
●AF_UNSPEC 未指定
type参数指定socket 的类型:
SOCK_STREAM:
流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
SOCK_DGRAM:
数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报UDP
SOCK RAW:
允许程序使用低层协议,原始套接字允许对底层协议如IP或ICMP 进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发
protocol
通常赋值”0“。
●0选择type类型对应的默认协议
●IPPROTO_TCPTCP传输协议
●IPPROTO_UDP UDP传输协议IPPROTO_SCTPSCTP传输协议●Sh0iCh0nTa15984016225
●IPPROTO_TIPC TIPC传输协议

二.bind()–地址

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)

bind()函数:IP号端口号与相应描述字赋值函数
#include <sys/types.h>
#include <sys/socket.h>
功能:
●用于绑定IP 地址和端口号到socketfd
参数:
●sockfd是一个socket描述符
●addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构这个地址结构根据地址创建socket 时的地址协议族的不同而不同

//ipv4对应的是:               
struct sockaddr{
				unisgned short   as_family;/协议族
				char             sa_data[14];//IP+端口
};
同等替换:
struct sockaddr_in {
			sa_family_t		sin_family;/*协议族*/
			in_port_t		sin_port;/*端口号*/
			struct in_addr sin_addr; /*IP地址结构体*/
			unsigned charsin_zero[8];/*填充 没有实际意义,只是为跟sockaddr结构在内存中对齐这样两者才能相互转换*/
}
cd /usr/include/  //到usr/include/查看
grep "struct sockaddr_in {" * -nir   
//找"struct sockaddr_in {",n是找出显示行号,r是递归查找,i是不区分大小写;

三.地址转换API

int inet_aton(const char* straddr,struct in_addr* addrp);
把字符串形式的"192.168.1.123"转为网络能识别的格式
char* inet_ntoa(struct in_addr inaddr);
把网络格式的ip地址转为字符串形式

四.listen监听

int listen(int sockfd,int backlog);

listen()函数:监听设置函数
#indude <sys/types.h>
#indude <sys/socket.h>
功能 :
●设置能处理的最大连接数,listen并未开始接受连线,只是设置socket的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接,主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数
●内核为任何一个给定监听套接字维护两个队列:
●未完成连接队列,每个这样的SYN报文段对应其中一项:城生主两强限出用大服器而服务器正在等待完成相应的CP三次据手过棍。这些套接字处于SYIREVD状在:
已完成连接队列,每个已完成TCP三次握手过程的客户端对应其中一项,这些套接字处于ESTABLISHED状态:
参数:
●sockfd
sockfd是socket系统调用返回的服务器端socket描述符
●backlog
backlog 指定在请求队列中允许的最大请求数

五.accept–连接

 #include <sys/types.h>/ See NOTES */
 #include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 
功能
	●accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。
参数
 ●sockfd
	sockfd是socket系统调用返回的服务器端socket描述符
 ●addr
 	用来返回已连接的对端(客户端)的协议地址
 ●addrlen
	客户端地址长度
返回值
●该函数的返回值是一个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一个参数是服务器监听套接字描述符。一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示TCP三次握手已完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。

六.数据收发

第一套API

 字节流读取函数在套接字通信中进行字节读取函数:read()write()。与I/O中的读取函数略有区别,因为它们输入或输出的字节数比可能比请求的少.
ssize_t write(int fd, const void*buf,size_t nbytes);
ssize_t read(int fd,void *buf,size_t nbyte);
/“说明
*函数均返回读或写的字节个数,出错则返回-1
*/
第一个将buf中的nbytes个字节写入到文件描述符fd中,成功时返回写的字节数。第二个为从fd中读取nbyte个字节到buf 中,返回实际所读的字节数。详细应用说明参考使用read write读写socket(套接字)。
网络I/0还有一些函数,例如:recv()/send()readv()/writev()recvmsg()/sendmsg()recvfrom()/sendto()(其中后两套一般用于UDP)

第二套API

1、在TCP套接字上发送数据函数:有连接
ssize_t send(int s,const void *msg,size_t len,int flags);
//包含3要素:套接字s,待发数据msg,数据长度len
//函数只能对处于连接状态的套接字使用,参数5为已建立好连接的套接字描述
//符,即accept函数的返回值
//参数msg指向存放待发送数据的缓冲区
//参数len为待发送数据的长度,参数flags为控制选项,一般设置为0
2、在TCP套接字上接收数据函数:有连接
 ssize_t recv(int s,void *buf,size_t len,int flags);
 //包含3要素:套接字s,接收缓冲区buf,长度len
//函数rec从参数s所指定的套接字描述符(必须是面向连接的套接字)上接收
//数据并保存到参数buf所指定的缓冲区
//参数len则为缓冲区长度,参数flags为控制选项,一般设置为0


七.客户端的connect函数

connect()函数:客户机连接主机
#include <sys/types.h>/* See NOTES*/
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能
●该函数用于绑定之后的client端(客户端),与服务器建立连接
参数
●sockfd
是目的服务器的sockect描述符
●addr
是服务器端的IP 地址和端口号的地址结构指针
●addrlen
地址长度常被设置为 sizeof(struct sockaddr)
返回值
●成功返回0,遇到错误时返回-1,并且errno中包含相应的错误码

八.图示

在这里插入图片描述

九.实例

server端,
可以通过在windows使用:

telnet "ip" 8989 查看是否能连接//telnet也是TCP协议
#include <arpa/inet.h>
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<linux/in.h>
#include <netinet/in.h>
int main()
{
        //1.socket
        int s_fd=socket(AF_INET,SOCK_STREAM,0);
        if(s_fd==-1){
                printf("socket failure\n");
                perror("why");
        }

        //2.bind
        struct sockaddr_in saddr;
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(8989);//因为要传到网络上,所以要调用htons把它变成网络字节序

        inet_aton("127.0.0.1",&saddr.sin_addr);//把字符串转换成网络能识别的,127.0.0.1是本机ip,一般要ifconfig查看
        int b=bind(s_fd,(struct sockaddr *)&saddr,sizeof(struct sockaddr));
        //因参数需要,所以saddr需要强转成(struct sockaddr *)
        if(b==-1){
                printf("bind failure");
                perror("why");
        }

        //3.listen
        listen(s_fd,10);//监听10个

        //4.accept
        int c_fd=accept(s_fd,NULL,NULL);//等待接收,没接收到会阻塞,接收到了返回客户端网络标识符


        printf("connect\n");

        while(1);


        return 0;
}

十.实现服务端和客户端之间的交流

服务端:

#include <arpa/inet.h>
#include<unistd.h>
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<linux/in.h>
#include <netinet/in.h>
#include<string.h>
int main()
{
        char writebuf[128]={0};
        char readbuf[128]={0};
        int len=sizeof(struct sockaddr *);
        //1.socket
        int s_fd=socket(AF_INET,SOCK_STREAM,0);
        if(s_fd==-1){
                printf("socket failure\n");
                perror("why");
        }

        //2.bind
        struct sockaddr_in caddr;
        struct sockaddr_in saddr;
        saddr.sin_family=AF_INET;
        saddr.sin_port=htons(8989);

        inet_aton("192.168.43.4",&saddr.sin_addr);
        int b=bind(s_fd,(struct sockaddr *)&saddr,sizeof(struct sockaddr));
        if(b==-1){
                printf("bind failure");
                perror("why");
        }

        //3.listen
        listen(s_fd,10);

        while(1){
                //4.accept
                int c_fd=accept(s_fd,(struct sockaddr *)&caddr,&len);
                if(c_fd==-1){
                        printf("accept failure");
                        perror("why");
                }else{
                        printf("%s connecting......\n",inet_ntoa(caddr.sin_addr));
                }
                if(fork()==0){
                        //5.read
                        memset(readbuf,'\0',128);
                        int n_read=read(c_fd,readbuf,128);
                        if(n_read==-1){
                                printf("read failure,");
                                perror("why");
                        }else{
                                printf("readbuf=%s\n",readbuf);
                        }
                        if(fork()==0){
                                //6.write
                                memset(writebuf,'\0',128);
                                scanf("%s",writebuf);
                                int writelen=strlen(writebuf);
                                int n_write=write(c_fd,writebuf,writelen);
                                break;
                        }
                }
        }
        return 0;
}
                                                                                                             

客户端:

#include<string.h>
#include<stdio.h>
#include <arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<linux/in.h>
#include<unistd.h>
int main()
{
        char writebuf[128]={0};
        int len=0;
        char readbuf[128]={0};
        //1.socket
        int c_fd=socket(AF_INET,SOCK_STREAM,0);
        if(c_fd==-1){
                printf("socket failure ");
                perror("why");
        }
        //2.connect
        struct sockaddr_in caddr;
        caddr.sin_family=AF_INET;
        caddr.sin_port=htons(8989);
        inet_aton("192.168.43.4",&caddr.sin_addr);
        if(connect(c_fd,(struct sockaddr *)&caddr,sizeof(struct sockaddr))==-1){

                printf("connect failure ");
                perror("why");
        }
        while(1){
                memset(writebuf,'\0',128);
                scanf("%s",writebuf);
                len=strlen(writebuf);
                write(c_fd,writebuf,len);
                if(fork()==0){
                        memset(readbuf,'\0',128);
                        read(c_fd,readbuf,128);
                        printf("server:%s\n",readbuf);
                        break;
                }
        }
        return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值