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协议的客户端/服务器程序的一般流程一般如下:

它基本上可以分为三个部分:

一、建立连接:

  1. 服务器调用socket()bind()listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态
  2. 客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答
  3. 服务器应答一个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;  }  




0 0