Linux编程基础——Socket编程
来源:互联网 发布:长短经哪个版本好 知乎 编辑:程序博客网 时间:2024/05/21 01:47
Linux下的Socket编程大体上包括Tcp Socket、Udp Socket即Unix Domain Socket这三种,其中TCP和UDP方式的Socket编程用于编写应用层的socket程序,是我们用得比较多的,Unix Domain Socket主要用于unix的本地通信。
TCP Socket
基于TCP协议的客户端/服务器程序的一般流程一般如下:
它基本上可以分为三个部分:
一、建立连接:
- 服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态
- 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答
- 服务器应答一个SYN-ACK段,客户端收到后从connect()返回,同时应答一个ACK段,服务器收到后从accept()返回。
二、传输数据:
建立连接后,TCP协议提供全双工的通信管道,服务器端和客户端根据协议可以通过read和write的反复调用实现数据的传输
三、关闭连接:
当数据传输已经完成后,服务器和客户端可以调用Close关闭连接,一端关闭连接后,另一端read函数则会返回0,可以根据这个特征来感应另一端的退出。
下面就以一个简单的EchoServer演示一下如何创建服务器端和客户端代码,其中和socket相关api都会高亮显示。
服务器端步骤:
1. socket(int domain,int type,int protocol):建立套接字;
2 .bind(int sockid,struct sockaddr *addrp,socklen_t addrlen):把本机地址和端口跟上一步建立的socket绑定在一起;
3. listen(int sockid,int qsize):监听某套接字;
4. fd=accept(int sockid,struct sockaddr *callerid,socklen_t *addrlenp):等待某套接字接收信息;
5. read(int fd,void *buf,size_t nbytes):从套接字接收数据;
6. close(fd) 和close(sockid)
#include <iostream>#include <arpa/inet.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>using namespace std; #define MAXLINE 80#define SERV_PORT 8000 int main(){ //设置一个socket地址结构server_addr,代表服务器internet地址, 端口 struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; //创建用于internet的流协议(TCP)socket,用server_socket代表服务器socket listenfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr));//把一段内存区的内容全部设置为0 servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 20); cout<<"Accepting connections ..."<<endl; cliaddr_len = sizeof(cliaddr); while (1) { connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); int n = read(connfd, buf, MAXLINE); if(n>0){ cout<<"received from "<<inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)) <<" at PORT "<<cliaddr.sin_port<<":"<<buf<<endl; } for (int i = 0; i < n; i++)//将从client接收到的字母转化为大写,回送给client buf[i] = toupper(buf[i]); n = write(connfd, buf, sizeof(buf)); } close(connfd); close(listenfd); }
客户端步骤:
1. socket():建立套接字;
2.connect(int sockid,struct sockaddr *serv_addrp,socklen_t addrlen):连接到服务器;
3. write(int sockfd,const void *buf,size_t nbytes):发送数据到服务器.
4. close(sockid);
#include <iostream>#include <string.h>#include <stdio.h>#include <arpa/inet.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>using namespace std;#define MAXLINE 80#define SERV_PORT 8000int main(int argc, char *argv[]){ char buf[MAXLINE]; int sockfd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in servaddr = {0}; servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); if (0 != connect(sockfd, (sockaddr *)&servaddr, sizeof(servaddr))) { cout<<"connected failed"<<endl; return 1; } char message[20]; cin>>message; int count = write(sockfd, message, sizeof(message));if(count > 0){ cout<<"send to server:"<<message<<endl;}else{ cout<<"fail send to server"<<endl; return 1;} count = read(sockfd, buf, sizeof(message));if(count > 0){ cout<<"response from server: "<<buf<<endl;} close(sockfd); return 0;}
UDP Socket
典型的UDP客户端/服务器通讯过程如下图所示:
由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,可能反而会需要更多代码。
服务端步骤:1:加载套接字库,创建套接字(socket());
2:绑定套接字到一个IP地址和一个端口上(bind());
3:等待和接收数据(sendto()/recvfrom());
4:关闭套接字,关闭加载的套接字库(close())。#include <iostream>#include <arpa/inet.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>using namespace std; #define MAXLINE 80#define SERV_PORT 8888 int main(){ //设置一个socket地址结构server_addr,代表服务器internet地址, 端口 struct sockaddr_in servaddr; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; //创建用于internet的数据报协议(UDP)socket,用server_socket代表服务器socket int serverfd = socket(AF_INET, SOCK_DGRAM, 0); bzero(&servaddr, sizeof(servaddr));//把一段内存区的内容全部设置为0 servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); int ret = bind(serverfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); if(ret < 0){ cout<<"fail bind"<<endl; return 0; } /* 定义一个地址,用于捕获客户端地址 */ struct sockaddr_in client_addr;socklen_t client_addr_length = sizeof(client_addr); while (1) { int n = recvfrom(serverfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,&client_addr_length); if(n>0){ cout<<"received from "<<inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)) <<" at PORT "<<client_addr.sin_port<<":"<<buf<<endl; for(int i = 0; i < n; i++)//将从client接收到的字母转化为大写,回送给client buf[i] = toupper(buf[i]); sendto(serverfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,sizeof(client_addr)); } } close(serverfd); return 0;}
客户端步骤:
1:创建一个套接字(socket);
2:向服务器发送数据(sendto);
3:关闭套接字;#include <iostream>#include <string.h>#include <stdio.h>#include <arpa/inet.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>using namespace std;#define MAXLINE 80#define SERV_PORT 8888int main(int argc, char *argv[]){ char buf[MAXLINE]; /* 服务端地址 */ sockaddr_in servaddr = {0}; servaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr); servaddr.sin_port = htons(SERV_PORT); //定义一个client socket int client_fd = socket(AF_INET, SOCK_DGRAM, 0); cin>>buf; int count = sendto(client_fd,buf,sizeof(buf),0,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(count>0){ cout<<"success send to server"<<endl; socklen_t server_add_len = sizeof(servaddr); int n = recvfrom(client_fd,buf,sizeof(buf),0,(struct sockaddr *)&servaddr,&server_add_len); if(n>0){ cout<<"response from server:"<<buf<<endl; } }else{ cout<<"fail send to server"<<endl; } close(client_fd); return 0;}
UNIX Socket
socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIXDomain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
下面是unix udp通信的例子:#include <iostream>#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> using namespace std;#define MAXLINE 60 int main() { /* delete the socket file */ unlink("server_socket"); /* create a UNIX socket */ int serverfd = socket(AF_UNIX, SOCK_DGRAM, 0); struct sockaddr_un server_addr; server_addr.sun_family = AF_UNIX; strcpy(server_addr.sun_path, "server_socket"); /* bind with the local file */ bind(serverfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); char buf[MAXLINE]; struct sockaddr_un client_addr; socklen_t len = sizeof(client_addr); while(1) { int n = recvfrom(serverfd,buf,sizeof(buf),0,(struct sockaddr *)&client_addr,&len); if(n>0){ cout<<"received:"<<buf<<endl; } } close(serverfd); return 0; }客户端:
#include <iostream>#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> using namespace std;#define MAXLINE 60 int main() { /* create a socket */ int client_fd = socket(AF_UNIX, SOCK_DGRAM, 0); /*server address*/ struct sockaddr_un servaddr; servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path, "server_socket"); char buf[MAXLINE]; cin>>buf; int count = sendto(client_fd,buf,sizeof(buf),0,(struct sockaddr *)&servaddr,sizeof(servaddr)); if(count>0){ cout<<"send success:"<<buf<<endl; } /* close the socket */ close(client_fd); return 0; }
- Linux编程基础——Socket编程
- Linux编程基础——Socket编程
- Linux编程基础——Socket编程
- Linux socket编程基础
- Linux socket编程基础
- linux socket 编程基础
- Linux下socket编程基础——socket地址API
- 网络编程基础——Socket编程
- Socket编程基础——Socket选项
- Linux 下Socket编程基础
- Linux 下Socket编程基础
- Linux 下Socket编程基础
- Linux下socket编程基础
- Linux 下Socket编程基础
- Linux 下Socket编程基础
- 【Linux基础】socket编程详解
- Linux下Socket编程一 socket 基础
- 嵌入式 Linux网络编程(一)——Socket网络编程基础
- 我与一只流浪猫的相遇
- singleTask 和singleInstance差异
- objc利用block实现链式编程方法
- Spring MVC Custom Validator Example
- NoSql——redis(高级操作)
- Linux编程基础——Socket编程
- Knockout应用开发指南 第三章:绑定语法(3)
- rabbitmq在高可用HA方面的方案总结
- hadoop是什么
- “#ifdef __cplusplus extern "C" { #endif”的定义
- E-RAB
- 网页设计规范
- 华为上机测试题(协议数据解析-java)
- viewpager的fragment嵌套viewpager的问题