zoukankan      html  css  js  c++  java
  • 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");
  • 相关阅读:
    leetcode 190 Reverse Bits
    vs2010 单文档MFC 通过加载位图文件作为客户区背景
    leetcode 198 House Robber
    记忆化搜索(DP+DFS) URAL 1183 Brackets Sequence
    逆序数2 HDOJ 1394 Minimum Inversion Number
    矩阵连乘积 ZOJ 1276 Optimal Array Multiplication Sequence
    递推DP URAL 1586 Threeprime Numbers
    递推DP URAL 1167 Bicolored Horses
    递推DP URAL 1017 Staircases
    01背包 URAL 1073 Square Country
  • 原文地址:https://www.cnblogs.com/zz1314/p/12960406.html
Copyright © 2011-2022 走看看