Linux 网络编程

网络编程

网络编程即如何在程序中实现两台或多台计算机之间的通信。

在进程间通信这一节,我们讲了管道、消息队列等IPC方式,不过只能限制在同一台主机上的进程间通信,满足不了不同主机之间的进程通信,而网络编程中的Socket正好可以满足。现在网络上各种各样的服务大多是基于Socket来完成通信的。

套接字Socket:简单来说是IP地址与端口的结合协议
组成与作用:在网络传输中用于唯一标识两个端点的链接。
端点:包括(IP+Port)
四个要素:客户端的地址、客户端的端口、服务器的地址、服务器端口。
在这里插入图片描述

端口号的作用?
主机怎么区分不同的网络服务?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。
实际上是通过IP地址+端口号来区分不同服务的。端口提供了一种访问通道。

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则是不可靠信道。

字节序
小端字节序:将低序字节存储在起始地址。
大端字节序:将高序字节存储在起始地址。

例子:在内存中双字0x01020304的存储方式
地址 4000 4001 4002 4003
小端 04 03 02 01
大端 01 02 03 04

Socket服务器和客户端的开发步骤
1、创建套接字
2、为套接字添加信息(IP地址和端口号)
3、监听网络连接
4、监听到有客户端接入,接受一个连接
5、数据交互
6、关闭套接字,断开连接

API介绍

1、创建并初始化Socket

#include <sys/types.h>
#include <sys/socket.h>
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_RAM:允许程序使用底层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

protocol 通常赋值0。

  • 0选择type类型对应的默认协议
  • IPPROTO_TCP:TCP传输协议
  • IPPROTO_UDP:UDP传输协议
  • IPPROTO_STCP:STCP传输协议
  • IPPROTO_TIPC:TIPC传输协议

返回
成功的话返回Scoket描述符Sockfd,失败则返回-1。

2、IP端口号与对应描述字赋值函数

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
功能:用于绑定IP地址和端口号到sockfd
参数
sockfd:Socket描述符
addr:指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族不同而不同。
addrlen:sockaddr结构体类型的长度大小
返回
成功返回0,失败返回-1,并设置errno

//ipv4对应的是:
struct sockaddr {
sa_family_t sa_family;//协议族 char
sa_data[14];//IP+端口
}
同等替换:
struct sockaddr_in
{
sa_family_t sa_family;//协议族
in_port_t sin_port;//端口
struct in_addr sin_addr;//IP地址结构体
unsigned char sin_zero[8]
//填充,没有实际意义,只是为跟sockaddr结构在内存中对齐,这样两者才能相互转换
}

地址转换
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//把字符串形式的”192.168.1.123”转为网络能识别的格式,即in_addr结构体类型 int inet_aton(const char *cp, struct in_addr *inp);
//把网络格式的ip地址转为字符串形式
char *inet_ntoa(struct in_addr in);

3、监听设置函数

#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:设置能处理的最大连接数,listen函数只能用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动要求与某个进程连接,只能监听是否有其他客户端进程与之连接,然后响应其连接请求,并对它做出处理。
参数
sockfd:socket描述符
backlog:在请求队列中允许的最大请求数。

4、连接

#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:函数由tcp服务器调用,用于从已完成连接队列队头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠。
参数
sockfd:socket描述符
addr:用于返回已连接的客户端的协议地址
addrlen:客户端地址长度。
返回
返回一个已连接的客户端套接字描述符,失败返回-1,并设置errno

5、字节流读写函数

与I/O中的读取函数略有区别,因为他们输入或输出的字节数可能比请求的少。
#include <unistd.h>
//函数均返回读或写的字节个数,出错则返回-1。
ssize_t read(int sockfd, void *buf, size_t count);
ssize_t write(int sockfd, const void *buf,size_t count);

#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
功能:基于UDP发送数据报,返回实际发送的数据长度,出错时返回-1
参数
sockfd:套接字描述符
buf:指向要发送数据的指针
len:数据长度
flags:通常为0
dest_addr:远端地址(IP地址和端口号)
addrlen:地址长度

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
功能:从UDP接收数据,返回实际接收的字节数,失败时返回-1
参数
sockfd:套接字描述符 buf:指向内存块的指针
len:内存块大小,以字节为单位
flags:通常为0
src_addr:远端地址(IP地址和端口号)
addrlen:地址长度

6、客户端连接主机

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:该函数用于绑定之后的client客户端,与服务器建立连接。
参数
sockfd:目的服务器的socket描述符
addr:服务器端的IP地址和端口号的地址结构指针
addrlen:地址长度常被设置为sizeof(struct sockaddr)
返回
成功返回0,失败返回-1,并设置errno

7、关闭套接字

#include <unistd.h>
int close(int sockfd)
返回
成功返回0,失败返回-1,并设置errno

8、字节序转换

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);//返回网络字节序的值
uint16_t htons(uint16_t hostshort);//返回网络字节序的值
uint32_t ntohl(uint32_t netlong);//返回主机字节序的值
uint16_t ntohs(uint16_t netshort);//返回主机字节序的值
h代表host,n代表net,s代表short(两个字节),l代表long(四个字节),通过上面四个函数来实现主机字节序与网络字节序之间的转换。

小技巧
//跳转到/usr/include路径
cd /usr/include
//在当前路径中的所有文件查找指定内容
grep “struct sockaddr_in {” * -nir
‘*’ 当前路径
-n 显示行号
-i 不区分字母大小
-r 递归查找

TCP协议Socket通信实例

//文件server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("arg error!\n");
                exit(-1);
        }
        int sockfd,c_fd;
        struct sockaddr_in s_addr,c_addr;
        socklen_t c_size;
        char* c_ip="";
        char buf[1024]="";
        int read_size=0;
        memset(&s_addr,0,sizeof(struct sockaddr_in));
        memset(&c_addr,0,sizeof(struct sockaddr_in));
        s_addr.sin_family=AF_INET;
        s_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(s_addr.sin_addr));
        sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(sockfd==-1)
        {
                printf("socket create fail!\n");
        }
        if(bind(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1)
        {
                printf("bind fail!\n");
                exit(-1);
        }
        else
        {
                printf("server:%s %d\n",argv[1],ntohs(s_addr.sin_port));
        }
        if(listen(sockfd,5)==-1)
        {
                printf("listen fail!\n");
        }
		c_size=sizeof(struct sockaddr_in);
        c_fd=accept(sockfd,(struct sockaddr*)&c_addr,&c_size);
        if(c_fd==-1)
        {
                printf("ip+port get error\n");
                perror("why");
                close(c_fd);
                close(sockfd);
                exit(-1);
        }
        c_ip=inet_ntoa(c_addr.sin_addr);
        printf("connect from:%s %d\n",c_ip,ntohs(c_addr.sin_port));
        while(1)
        {
                memset(buf,'\0',1024);
                read_size=read(c_fd,buf,1024);
                strncpy(buf,buf,read_size);
                printf("client:%s\n",buf);
                if(strcmp(buf,"exit")==0)
                {
                        printf("client exit!\n");
                        break;
                }
        }
        close(c_fd);
        close(sockfd);
        return 0;
}
//文件client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
int main(int argc,char* argv[])
{
        if(argc!=3) 
        {
                printf("arg error!\n");
                exit(-1);
        }
        int client_fd;
        struct sockaddr_in client_addr;
        char buf[1024]="";
        memset(&client_addr,0,sizeof(struct sockaddr_in));
        client_fd=socket(AF_INET,SOCK_STREAM,0);
        client_addr.sin_family=AF_INET;
        client_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(client_addr.sin_addr));
        connect(client_fd,(struct sockaddr*)&client_addr,sizeof(struct sockaddr_in));
        while(1)
        {
                printf("please input data to server:\n");
                scanf("%s",buf);
                write(client_fd,buf,strlen(buf));
                if(strcmp(buf,"exit")==0)
                {
                        break;
                }
        }
        printf("socket exit!\n");
        close(client_fd);
        return 0;
}

服务器端:

Ubuntu@Embed_Learn:~/learn/socket$./server 127.0.0.1 8888
server:127.0.0.1 8888
connect from:127.0.0.1 40568
client:hello
client:goodbye
client:exit
client exit!

客户端:

Ubuntu@Embed_Learn:~/learn/socket$ ./client 127.0.0.1 8888
please input data to server:
hello
please input data to server:
goodbye
please input data to server:
exit
socket exit!

UDP协议Socket通信实例

//文件server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("arg error!\n");
                exit(-1); 
        }
        int server_fd;
        struct sockaddr_in server_addr,client_addr;
        int sockaddr_size=sizeof(struct sockaddr_in);
        char buf[1024]="";
        memset(&server_addr,0,sizeof(struct sockaddr_in));
        server_addr.sin_family=AF_INET;
        server_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(server_addr.sin_addr));
        server_fd=socket(AF_INET,SOCK_DGRAM,0);
        if(bind(server_fd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr_in))==-1)
        {
                printf("bind fail!\n");
                close(server_fd);
                exit(-1);
        }
        int read_size=recvfrom(server_fd,(void*)buf,sizeof(buf),0,(struct sockaddr*)&client_addr,&sockaddr_size);
       	printf("client ip:%s port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
        printf("bytes:%d recv from client:%s\n",read_size,buf);
        int write_size=sendto(server_fd,buf,strlen(buf),0,(struct sockaddr*)&client_addr,sockaddr_size);
        printf("bytes:%d send from client:%s\n",write_size,buf);
        close(server_fd);
        return 0;
}
//文件client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> 
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("arg error!\n");
                exit(-1); 
        }
        int client_fd;
        struct sockaddr_in server_addr; 
        int sockaddr_size=sizeof(struct sockaddr_in);
        char buf[1024]="hello!";
        memset(&server_addr,0,sizeof(struct sockaddr_in));
        server_addr.sin_family=AF_INET;
        server_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(server_addr.sin_addr));
        client_fd=socket(AF_INET,SOCK_DGRAM,0);
         int write_size=sendto(client_fd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sockaddr_size);
        printf("bytes:%d send from server:%s\n",write_size,buf);
        memset(buf,'\0',sizeof(buf));
        int read_size=recvfrom(client_fd,(void*)buf,sizeof(buf),0,NULL,NULL);
        printf("bytes:%d recv from server:%s\n",read_size,buf);
        close(client_fd);
        return 0;
}

服务器端:

Ubuntu@Embed_Learn:~/learn/socket$./udpserver 127.0.0.1 8888
client ip:127.0.0.1 port:46415
bytes:6 recv from client:hello!
bytes:6 send from client:hello!

客户端:

Ubuntu@Embed_Learn:~/learn/socket$ ./udpclient 127.0.0.1 8888
bytes:6 send from server:hello!
bytes:6 recv from server:hello!

例程:服务器端对多客户端收发消息实例

//文件server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("arg error!\n");
                exit(-1);
        }
        int sockfd,c_fd;
        struct sockaddr_in s_addr,c_addr;
        socklen_t c_size;
        pid_t pid1,pid2;
        char* c_ip;
        char read_buf[1024]="";
        char write_buf[1024]="";
        int read_size=0;
        int write_size=0;
        memset(&s_addr,0,sizeof(struct sockaddr_in));
        memset(&c_addr,0,sizeof(struct sockaddr_in));
        s_addr.sin_family=AF_INET;
        s_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(s_addr.sin_addr));
        sockfd=socket(AF_INET,SOCK_STREAM,0);
        if(sockfd==-1)
        {
                printf("socket create fail!\n");
        }
        if(bind(sockfd,(struct sockaddr*)&s_addr,sizeof(struct sockaddr_in))==-1)
        {
                printf("bind fail!\n");
                exit(-1);
        }
        else
        {
                printf("server:%s %d\n",argv[1],ntohs(s_addr.sin_port));
        }
        if(listen(sockfd,5)==-1)
        {
                printf("listen fail!\n");
        }
        c_size=sizeof(struct sockaddr_in);
        while(1)
        {
                if((c_fd=accept(sockfd,(struct sockaddr*)&c_addr,&c_size))==-1)
                {
                        printf("ip+port get error\n");
                        perror("why");
                        close(c_fd);
                }
                else
                {
                        c_ip=inet_ntoa(c_addr.sin_addr);
                        printf("connect from:%s %d\n",c_ip,ntohs(c_addr.sin_port));
                        if((pid1=fork())<0)
                        {
                                printf("fork fail!\n");
                                close(c_fd);
                        }
                        else if(pid1==0)
                        {
                                if((pid2=fork())<0)
                                {
                                        printf("fork fail!\n");
                                        exit(-1);
                                }
                                else if(pid2==0)
                                {
                                        while(1)
                                        {
                                                memset(read_buf,'\0',1024);
                                                if((read_size=read(c_fd,read_buf,1024))==-1)
                                                {
                                                        printf("read error!\n");
                                                }
                                                strncpy(read_buf,read_buf,read_size);
                                                printf("bytes:%d client:%s\n",read_size,read_buf);
                                                if(strcmp(read_buf,"exit")==0)
                                                {
                                                        printf("read quit!\n");
                                                        close(c_fd);
                                                        exit(0);
                                                }
                                        }
                                }
                                else
                                {
                                        while(1)
                                        {
                                                scanf("%s",write_buf);
                                                if(write(c_fd,write_buf,strlen(write_buf))==-1)
                                                {
                                                        printf("write error!\n");
                                                }
                                                if(strcmp(write_buf,"exit")==0)
                                                {
                                                         printf("write quit!\n");
                                                         close(c_fd);
                                                         exit(0);
                                                }
                                        }
                                }
                        }
                }
        }
        close(sockfd);
        return 0;
}
//文件client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>  
#include <string.h> 
int main(int argc,char* argv[])
{
        pid_t pid;
        if(argc!=3) 
        {
                printf("arg error!\n"); 
                exit(-1);
        }
        int client_fd;
        int read_size=0;
        int write_size=0;
        struct sockaddr_in client_addr;
        char write_buf[1024];
        char read_buf[1024];
        memset(&client_addr,0,sizeof(struct sockaddr_in));
        client_fd=socket(AF_INET,SOCK_STREAM,0);
        client_addr.sin_family=AF_INET;
        client_addr.sin_port=htons(atoi(argv[2]));
        inet_aton(argv[1],&(client_addr.sin_addr));
        int flag=connect(client_fd,(struct sockaddr*)&client_addr,sizeof(struct sockaddr));
        if(flag==-1)
        {
                printf("connect fail!\n");
                exit(-1);
        }
        printf("connect:%s %s success!\n",argv[1],argv[2]);
        if((pid=fork())==-1)
        {
                printf("fork fail!\n");
        }
        else if(pid>0)
        {
                while(1)
                {
                        scanf("%s",write_buf);
                        if(write(client_fd,write_buf,strlen(write_buf))==-1)
                        {
                                printf("write error!\n");
                        }
                        if(strcmp(write_buf,"exit")==0)
                        {
                                printf("write quit!\n");
                                break;
                        }
                }
        }
        else
        {
                while(1)
                {
                         memset(read_buf,'\0',1024);
                         if((read_size=read(client_fd,read_buf,1024))==-1)
                         {
                                printf("read quit!\n");
                         }
                         strncpy(read_buf,read_buf,read_size);
                         printf("bytes:%d server:%s\n",read_size,read_buf);
                         if(strcmp(read_buf,"exit")==0)
                         {
                                printf("read quit!\n");
                                break;
                         }
                }
        }
        close(client_fd);
        return 0;
}

服务器端:

Ubuntu@Embed_Learn:~/learn/socket$ ./server 127.0.0.1 8888
server:127.0.0.1 8888
connect from:127.0.0.1 57695
bytes:5 client:hello
bytes:9 client:howareyou
I am ok      
bytes:2 client:ok
goodbye
bytes:7 client:goodbye
bytes:4 client:exit
exit
write quit!
read quit!

客户端:

Ubuntu@Embed_Learn:~/learn/socket$ ./client 127.0.0.1 8888
connect:127.0.0.1 8888 success!
hello
how are you
bytes:5 server:Iamok
ok
bytes:7 server:goodbye
goodbye
bytes:4 server:exit
read quit!
exit
write quit! 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一盆电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值