• socket网络编程--基础应用篇


    首先我们先介绍一些socket编程的基本API,利用这个API实现一个简单的C-S模型,在这个模型中,服务器接收到客户端的消息后,会将接受到的字符串进行大小写转换,然后发送给客户端,并打印。

    1、socket函数--创建一个套接字

    1 #include <sys/types.h>          /* See NOTES */
    2 #include <sys/socket.h>
    3 
    4 int socket(int domain, int type, int protocol);

      参数分析:

        domain:指定一个通信域,通常用Ipv4协议,使用 AF_INET

        type:指定流的方式,tcp是基于流的传输,通常传入SOCK_STREAM,udp基于数据报的传输,传入SOCK_DGRM

        protocol:这个参数是用于一个第二选择,我们传入0即可

      return value:

        成功的话,返回一个套接字的文件描述符,失败返回-1.

    2、bind函数--将套接字绑定到地址(ip+port)上

    1 #include <sys/types.h>          /* See NOTES */
    2 #include <sys/socket.h>
    3 
    4 int bind(int sockfd, const struct sockaddr *addr,
    5          socklen_t addrlen);

      参数分析:

        sockfd:套接字文件描述符

        addr:通过创建一个结构体struct sockaddr_in srvaddr,然后强转成定义的sockaddr类型即可

        addrlen:前面创建套接字地址结构体的大小

      返回值:

        成功返回0,失败返回-1

    3、listen函数--监听连接,定义最大连接数

    1 #include <sys/types.h>          /* See NOTES */
    2 #include <sys/socket.h>
    3 
    4 int listen(int sockfd, int backlog);

      参数分析:

        sockfd:套接字文件描述符

        backlog:最大连接数,SOMAXCONN,值为128

      返回值:

        成功返回0,失败返回-1

    4、accept函数--接受一个连接

    1 #include <sys/types.h>          /* See NOTES */
    2 #include <sys/socket.h>
    3 
    4 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

      参数分析:

        sockfd:套接字文件描述符

        addr:通过创建一个结构体struct sockaddr_in srvaddr,然后强转成定义的sockaddr类型即可

        addrlen:前面创建套接字地址结构体的大小

      返回值:

        成功返回一个用于通信的文件描述符,失败返回-1

    5、accept函数--建立一个连接

    1 #include <sys/types.h>          /* See NOTES */
    2 #include <sys/socket.h>
    3 
    4 int connect(int sockfd, const struct sockaddr *addr,
    5             socklen_t addrlen);

      参数分析:

        sockfd:套接字文件描述符

        addr:通过创建一个结构体struct sockaddr_in srvaddr,然后强转成定义的sockaddr类型即可

        addrlen:前面创建套接字地址结构体的大小

      返回值:

        成功返回0,失败返回-1

    SERVER.CPP

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <sys/types.h>
     4 #include <sys/socket.h>
     5 #include <stdlib.h>
     6 #include <arpa/inet.h>
     7 #include <netinet/in.h>
     8 #include <string.h>
     9 #include <unistd.h>
    10 using namespace std;
    11 
    12 
    13 #define ERR_EXIT(m)
    14     do
    15     {
    16         perror(m);
    17         exit(EXIT_FAILURE);
    18     }while(0);
    19 
    20 int main(int argc, char* argv[]){
    21     if(argc < 2){
    22         cout << "please add port;" <<endl;
    23         exit(-1);
    24     }
    25     int port = atoi(argv[1]);
    26     //创建一个套接字
    27     int srvfd = socket(AF_INET, SOCK_STREAM, 0);
    28     if(srvfd == -1){
    29         ERR_EXIT("socket");
    30     }
    31     //定义一个结构体并填充
    32     struct sockaddr_in srvaddr;
    33     srvaddr.sin_family = AF_INET;
    34     srvaddr.sin_port = htons(port);
    35     srvaddr.sin_addr.s_addr = INADDR_ANY;
    36     //将套接字绑定到地址上
    37     int ret = bind(srvfd, (const struct sockaddr*)&srvaddr, sizeof(srvaddr));
    38     if(ret == -1){
    39         ERR_EXIT("bind");
    40     }
    41     //监听套接字
    42     ret = listen(srvfd, SOMAXCONN);
    43     if(ret == -1){
    44         ERR_EXIT("listen");
    45     }
    46     
    47     struct sockaddr_in sockaddr;
    48     socklen_t socklen = sizeof(sockaddr);
    49     //定义一个套接字,通常为已经连接的套接字
    50     int conn;
    51     conn = accept(srvfd, (struct sockaddr*)&sockaddr, &socklen);
    52     if(conn == -1){
    53         ERR_EXIT("conn");
    54     }
    55     else{
    56         cout << "peer ip addr = " << inet_ntoa(sockaddr.sin_addr) << ", port = " << htons(sockaddr.sin_port) << endl;
    57     }
    58     //循环获取数据,发送数据
    59     char recvbuf[1024] = {0};
    60     while(1){
    61         memset(recvbuf, 0, 1024);
    62         int ret = read(conn, recvbuf, 1024);
    63         fputs(recvbuf, stdout);
    64         for(int i = 0; i < strlen(recvbuf); i++){
    65             if(recvbuf[i] >= 'a' && recvbuf[i] <= 'z'){
    66                 recvbuf[i] -= 32;
    67             }
    68             else if(recvbuf[i] >= 'A' && recvbuf[i] <= 'Z'){
    69                 recvbuf[i] += 32;
    70             }
    71         }
    72         write(conn, recvbuf, strlen(recvbuf));
    73     }
    74     //关闭套接字
    75     close(srvfd);
    76     close(conn);
    77     return 0;
    78 }

    CLIENT.CPP

     1 #include <iostream>
     2 #include <sys/types.h>
     3 #include <sys/socket.h>
     4 #include <stdio.h>
     5 #include <unistd.h>
     6 #include <netinet/in.h>
     7 #include <arpa/inet.h>
     8 #include <stdlib.h>
     9 #include <string.h>
    10 using namespace std;
    11 
    12 #define ERR_EXIT(m)
    13     do
    14     {
    15         perror(m);
    16         exit(EXIT_FAILURE);
    17     }while(0);
    18 
    19 int main(int argc, char *argv[])
    20 {
    21     if(argc < 2){
    22         ERR_EXIT("need port");
    23     }
    24     int port = atoi(argv[1]);
    25     //创建一个套接字
    26     int cltfd = socket(AF_INET, SOCK_STREAM, 0);
    27     if(cltfd == -1){
    28         ERR_EXIT("socket");
    29     }
    30     //定义一个地址结构
    31     struct sockaddr_in cltaddr;
    32     cltaddr.sin_family = AF_INET;
    33     cltaddr.sin_port = htons(port);
    34     cltaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    35     //进行连接
    36     int ret = connect(cltfd, (const struct sockaddr*)&cltaddr, sizeof(cltaddr));
    37     if(ret == -1){
    38         ERR_EXIT("connect");
    39     }
    40     else{
    41         cout << "connect success" << endl;
    42     }
    43     char sendbuf[1024] = {0};
    44     char recvbuf[1024] = {0};
    45     //从标准输入中读入
    46     while(fgets(sendbuf, 1024, stdin) != NULL){
    47         write(cltfd, sendbuf, strlen(sendbuf));
    48         if(read(cltfd, recvbuf, 1024) > 0){
    49             cout << "get info from server" << endl;
    50             fputs(recvbuf, stdout);
    51         }
    52         memset(sendbuf, 0, 1024);
    53         memset(recvbuf, 0, 1024);
    54     }
    55     close(cltfd);
    56     return 0;
    57 }

     补充:

    当我们使用ctrl+c结束进程后,服务器是无法立即重启的,如果重启服务器会提示---bind: Address already in use

    原因就在于服务器重新启动时需要绑定地址,而这个时候网络正处于TIME_WAIT状态,只有这个状态退出后,该地址才能被重新绑定。TIME_WAIT的时间是两个MSL,大约是1~4分钟。若每次服务器重启都需要等待TIME_WAIT结束那就太不合理了,好在选项SO_REUSEADDR能够解决这个问题。

    服务器端尽可能使用REUSEADD,在bind()之前调用setsockopt来设置SO_REUSEADDR套接字选项,使用SO_REUSEADDR选项可以使不必等待TIME_WAIT状态消失就可以重启服务器。

    1 /*设置地址重复使用*/
    2 int on = 1; //on为1表示开启
    3 if(setsockopt(listenfp ,SOL_SOCKET,SO_REUSEADDR,&on,sieof(on))<0)
    4     ERR_EXIT("setsockopt error");
  • 相关阅读:
    内置函数
    win10 下安装meteror
    每日十问(3)
    白话带你理解什么是编程
    什么是对象的方法
    Python之列表推导式
    英语对学习软件开发重要吗?
    python3中的range函数返回的是列表吗?
    文件读写
    神奇的字符编码
  • 原文地址:https://www.cnblogs.com/zz1314/p/12960406.html
Copyright © 2020-2023  润新知