liunx之socket网络编程
2021/4/17 20:30:05
本文主要是介绍liunx之socket网络编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
前言:socket网络编程是一门技术,它主要是在网络通信中经常用到。与进程间通信最大的区别是socket网络编程是基于多机信息传递的,而前者基于单机。
一、了解TCP和UDP的区别。
①TCP面向连接(如打电话要先拨号建立连接;UDP是无连接的,即发送数据之前不需要建立连接。
②TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
③TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)。
④每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信。
⑤TCP首部开销20字节;UDP的首部开销小,只有8个字节。
⑥TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
二、socket编程使用的API
1.字节序转换API
uint16_t htons(uint16_t host16bitvalue); //返回网络字节序的值 uint32_t htonl(uint32_t 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指定地址让操作系统自己获取
2.地址转换API
int inet_aton(const char * straddr,struct in_addr *addrp) 说明:把字符串形式的"192.168.1.123"转换为网络能识别的格式 第一个参数:IP地址的字符串。 第二个参数:字符串的地址 char *inet_ntoa(struct in_addr inaddr); 把网络格式的ip地址转为字符串形式
3.创建通信端点 socket
int socket(int domain, int type, int protocol); 第一个参数为:指定一个通信域,通常使用AF_INET(IPv4 Internet protocols) 第二个参数为:socket的制定类型,通常使用SOCK_STREAM(提供有序的、可靠的、双向的、基于连接的字节流。可以采用带外数据传输机制支持。) 第三个参数为:参数protocol用来指明所要接收的协议包,通常写0用于接收任何的IP数据包。其中的校验和和协议分析由程序自己完成 返回值:如果成功,将返回新套接字的文件描述符。若失败则返回-1。 eg: socket(AF_INET,SOCK_STREAM,0);
4.将名称绑定到套接字 bind
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 第一个参数为:创建通信端点时返回的文件标识符。 第二个参数为:addr为结构体变量的指针,这里我们使用 sockaddr_in 结构体,然后再强制转换为 sockaddr 类型。 sockaddr_in 结构体 struct sockaddr_in{ sa_family_t sin_family; //地址族(Address Family),也就是地址类型 uint16_t sin_port; //16位的端口号 struct in_addr sin_addr; //32位IP地址 char sin_zero[8]; //不使用,一般用0填充 }; 第三个参数为:addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。 返回值:如果成功,则返回0。出现错误时,返回-1,并设置errno适当。 eg:bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
5.监听套接字上的连接 listen
int listen(int sockfd, int backlog); 第一个参数为:创建通信端点时返回的文件标识符。 第二个参数为:backlog参数定义了待定队列的最大长度sockfd的连接可能会增加。如果连接请求到达时如果队列已满,客户端可能会收到一个指示为ECONNRE‐的错误融合或,如果底层协议支持重传,请求可以被忽略,以便以后重新尝试连接时成功。 返回值:如果成功,则返回0。出现错误时,返回-1,并设置errno适当。 eg:listen(s_fd,10);
6.接收套接字上的连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 第一个参数为:创建通信端点时返回的文件标识符。 第二个参数为:参数addr是指向sockaddr结构体的指针,需要转换类型。若为NULL,则addrlen也为空 第三个参数为:adr长度的地址. 返回值:如果成功,这些系统调用将返回一个非负整数a被接受套接字的描述符。错误时,返回-1,errn为适当的设置。 eg: int adr=sizeof(struct sockaddr_in); a_fd=accept(s_fd,(struct sockaddr *)&a_addr,&adr);
7.在套接字上发起连接 connect
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 第一个参数为:创建通信端点时返回的文件标识符。 第二个参数为:addr为要访问的地址。 第三个参数为:要访问地址的长度。 返回值:如果成功,则返回0。出现错误时,返回-1,并设置errno适当。
三、服务器端的构建(双人)
①创建通信端点套接字
②将名称绑定到套接字(IP地址和端口号)
③监听连接
④接收套接字的连接
⑤数据交互(读取、发送)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> //#include <linux/in.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main(int argc,char **argv) { int s_fd; int a_fd; int n_read; char readbuff[128]; char msg[128]={0}; struct sockaddr_in s_addr; struct sockaddr_in a_addr; if(argc!=3){ printf("progrm is not good\n"); exit(-1); } memset(&s_addr,0,sizeof(struct sockaddr_in)); //reset memset(&a_addr,0,sizeof(struct sockaddr_in)); //reset //1.socket s_fd=socket(AF_INET,SOCK_STREAM,0); if(s_fd == -1){ perror("socket"); exit(-1); } //2.bind s_addr.sin_family=AF_INET; s_addr.sin_port=htons(atoi(argv[2])); //s_addr.sin_addr.s_addr=("127.0.0.1"); inet_aton(argv[1],&s_addr.sin_addr); bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in)); //3.listen listen(s_fd,10); //4.accept int adr=sizeof(struct sockaddr_in); while(1){ a_fd=accept(s_fd,(struct sockaddr *)&a_addr,&adr); if(a_fd==-1){ perror("accept"); } printf("get connect:%s\n",inet_ntoa(a_addr.sin_addr)); if(fork()==0){ if(fork()==0){ while(1){ //write memset(msg,0,sizeof(msg)); printf("input: "); gets(msg); write(a_fd,msg,strlen(msg)); } } //read while(1){ memset(readbuff,0,sizeof(readbuff)); n_read=read(a_fd,readbuff,128); if(n_read==-1){ perror("read"); }else{ printf("get message from client:%d,%s\n",n_read,readbuff); } } break; } } return 0; }
四、接收端的构建(双人)
①创建通信端点
②发起连接
③发送数据
④读取数据
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <errno.h> //#include <linux/in.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main(int argc,char **argv) { int c_fd; int n_read; char readbuff[128]; char msg[128]={0}; struct sockaddr_in c_addr; if(argc!=3){ printf("progrm is not good\n"); exit(-1); } memset(&c_addr,0,sizeof(struct sockaddr_in)); //reset //1.socket c_fd=socket(AF_INET,SOCK_STREAM,0); if(c_fd == -1){ perror("socket:\n"); exit(-1); } //2.connect c_addr.sin_family=AF_INET; c_addr.sin_port=htons(atoi(argv[2])); inet_aton(argv[1],&c_addr.sin_addr); if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr))==-1){ perror("connect\n"); exit(-1); } while(1){ if(fork()==0){ while(1){ memset(msg,0,sizeof(msg)); printf("input: "); gets(msg); //3.write write(c_fd,msg,strlen(msg)); } } while(1){ //3.read memset(readbuff,0,sizeof(readbuff)); n_read=read(c_fd,readbuff,128); if(n_read==-1){ printf("perror:\n"); }else{ printf("I get message from server:%d,context:%s\n",n_read,readbuff); } } } return 0; }
进行多机通讯时只需要修改服务器端
①定义mark来标识用户端号数
②更改accept
//4.accept int adr=sizeof(struct sockaddr_in); while(1){ a_fd=accept(s_fd,(struct sockaddr *)&a_addr,&adr); if(a_fd==-1){ perror("accept"); } mark++; printf("get connect:%s\n",inet_ntoa(a_addr.sin_addr)); if(fork()==0){ if(fork()==0){ while(1){ //write sprintf(msg,"Welcom No.%d client",mark); //当前用户端号数 write(a_fd,msg,strlen(msg)); sleep(3); } } //read while(1){ memset(readbuff,0,sizeof(readbuff)); n_read=read(a_fd,readbuff,128); if(n_read==-1){ perror("read"); }else{ printf("get message from client:%d,%s\n",n_read,readbuff); } } break; } }
这篇关于liunx之socket网络编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享