linux 编程 ———网络编程(Socket 编程)客户端与服务端实现源码

15 篇文章 1 订阅
6 篇文章 0 订阅

文档声明:
以下资料均属于本人在学习过程中产出的学习笔记,如果错误或者遗漏之处,请多多指正。并且该文档在后期会随着学习的深入不断补充完善。感谢各位的参考查看。


笔记资料仅供学习交流使用,转载请标明出处,谢谢配合。
如果存在相关知识点的遗漏,可以在评论区留言,看到后将在第一时间更新。
作者:Aliven888

客户端

// client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#define SERVER_PORT 20001
#define SERVER_IP "127.0.0.1"

int main()
{
    int sock_fd;
    struct sockaddr_in addr_server;
    if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    printf("sock successful\n");

    //设置socket参数, 根据需求选择自己需要的
    {
        // 1.设置调用close(socket)后,仍可继续重用该socket。调用close(socket)一般不会立即关闭socket,而经历TIME_WAIT的过程。
        bool bReuseaddr = TRUE;
        setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(bool) );
        // 2. 如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:
        BOOL bDontLinger = FALSE;
        setsockopt(sock_fd, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(bool));

        //3. 超时等待设置
        int iSecond = 1;
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&iSecond, sizeof(iSecond));  //recv timeout
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&iSecond, sizeof(iSecond));  //send timeout

        //4. 缓冲区设置
        // socket缓冲区的概念:
        //      socket编程基于传输层,是应用层和传输层之间的一个抽象层。在使用socket API时,
        //      实际上每创建一个socket,都会分配两个缓冲区,输入缓冲区和输出缓冲区(大小一般是8K),
        //      Linux下一切皆文件的思想,两台主机在进行通信时,write函数是向缓冲区里写,read函数是从缓冲区里读,
        //      至于缓冲区里的数据什么时候被传输,有没有达到目标主机,这些都交给传输层的TCP/UDP来做。
        int iRecvBufLen = 32 * 1024; //设置为32K
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (const char*)&iRecvBufLen, sizeof(int) );

        int iSendBufLen = 32*1024; //设置为32K
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&iSendBufLen, sizeof(int) );

        // 5.在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:
        int iZero = 0;
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&iZero, sizeof(iZero) );
        
        // 6.在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:
        int iZero = 0;
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&iZero, sizeof(int) );
        
        // 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
        bool bBroadcast = true;
        setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bool));

        // 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只
        // 有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)
        bool bConditionalAccept = true;
        setsockopt(sock_fd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char*)&bConditionalAccept, sizeof(bool));

        // 9.如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了close(socket),以前一般采取的措施是shutdown(s,SD_BOTH),但是数
        // 据将会丢失。 某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:
        struct linger {
        u_short l_onoff;
        u_short l_linger;
        };
        struct linger m_sLinger;
        m_sLinger.l_onoff = 1; //在调用close(socket)时还有数据未发送完,允许等待
        // 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
        m_sLinger.l_linger = 5; //设置等待时间为5秒
        setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(struct linger));
    }

 
    memset(&addr_server, 0, sizeof(addr_server));
    addr_server.sin_family = AF_INET;
    addr_server.sin_port = htons(SERVER_PORT);
    addr_server.sin_addr.s_addr = inet_addr(SERVER_IP);
 
    if(connect(sock_fd, (struct sockaddr *)&addr_server, sizeof(struct sockaddr))<0)
    {
        perror("connect");
        exit(1);
    }
    printf("connect successful\n");
    send(sock_fd, "ok", 2, 0);

    //如果需要接收数据的话,可以再加个 recv
    //int len = recv(sock_fd, buf, sizeof(buf), 0);
    //处理 recv 接收的内容
    
    close(sock_fd);
}

服务端

// server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 
#define PORT 20001
 
int main()
{
    int sock_fd, client_fd;
    int addr_client_len;
    struct sockaddr_in addr_server, addr_client;
	
    addr_client_len = sizeof(struct sockaddr);
    if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        exit(1);
    }
    printf("sock successful\n");

    //设置socket参数, 根据需求选择自己需要的
    {
        // 1.设置调用close(socket)后,仍可继续重用该socket。调用close(socket)一般不会立即关闭socket,而经历TIME_WAIT的过程。
        bool bReuseaddr = TRUE;
        setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&bReuseaddr, sizeof(bool) );
        // 2. 如果要已经处于连接状态的soket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:
        BOOL bDontLinger = FALSE;
        setsockopt(sock_fd, SOL_SOCKET, SO_DONTLINGER, (const char*)&bDontLinger, sizeof(bool));

        //3. 超时等待设置
        int iSecond = 1;
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&iSecond, sizeof(iSecond));  //recv timeout
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (const char*)&iSecond, sizeof(iSecond));  //send timeout

        //4. 缓冲区设置
        // socket缓冲区的概念:
        //      socket编程基于传输层,是应用层和传输层之间的一个抽象层。在使用socket API时,
        //      实际上每创建一个socket,都会分配两个缓冲区,输入缓冲区和输出缓冲区(大小一般是8K),
        //      Linux下一切皆文件的思想,两台主机在进行通信时,write函数是向缓冲区里写,read函数是从缓冲区里读,
        //      至于缓冲区里的数据什么时候被传输,有没有达到目标主机,这些都交给传输层的TCP/UDP来做。
        int iRecvBufLen = 32 * 1024; //设置为32K
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (const char*)&iRecvBufLen, sizeof(int) );

        int iSendBufLen = 32*1024; //设置为32K
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (const char*)&iSendBufLen, sizeof(int) );

        // 5.在发送数据的时,不执行由系统缓冲区到socket缓冲区的拷贝,以提高程序的性能:
        int iZero = 0;
        setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, (char *)&iZero, sizeof(iZero) );
        
        // 6.在接收数据时,不执行将socket缓冲区的内容拷贝到系统缓冲区:
        int iZero = 0;
        setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, (char *)&iZero, sizeof(int) );
        
        // 7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
        bool bBroadcast = true;
        setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, (const char*)&bBroadcast, sizeof(bool));

        // 8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可以设置connect()延时,直到accpet()被调用(此设置只
        // 有在非阻塞的过程中有显著的作用,在阻塞的函数调用中作用不大)
        bool bConditionalAccept = true;
        setsockopt(sock_fd, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, (const char*)&bConditionalAccept, sizeof(bool));

        // 9.如果在发送数据的过程中send()没有完成,还有数据没发送,而调用了close(socket),以前一般采取的措施是shutdown(s,SD_BOTH),但是数
        // 据将会丢失。 某些具体程序要求待未发送完的数据发送出去后再关闭socket,可通过设置让程序满足要求:
        struct linger {
        u_short l_onoff;
        u_short l_linger;
        };
        struct linger m_sLinger;
        m_sLinger.l_onoff = 1; //在调用close(socket)时还有数据未发送完,允许等待
        // 若m_sLinger.l_onoff=0;则调用closesocket()后强制关闭
        m_sLinger.l_linger = 5; //设置等待时间为5秒
        setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, (const char*)&m_sLinger, sizeof(struct linger));
    }
 
    memset(&addr_server, 0, sizeof(addr_server));
    addr_server.sin_family = AF_INET;
    addr_server.sin_port = htons(PORT);
    addr_server.sin_addr.s_addr = htonl(INADDR_ANY);
 
    if(bind(sock_fd, (struct sockaddr *)&addr_server, sizeof(struct sockaddr_in)) < 0)
    {
        perror("bind");
        exit(1);
    }
 
    printf("bind sucessful\n");
 
    if(listen(sock_fd, 5))
    {
        perror("listen");
        exit(1);
    }
    printf("listen sucessful\n");
    char buf[1024];
    while(1)
    {
	memset(buf, '\0', 1024);
        if((client_fd = accept(sock_fd, (struct sockaddr *)&addr_client, &addr_client_len)) < 0)
        {
            perror("accept");
            exit(1);		
        }
	int len = recv(client_fd, buf, sizeof(buf), 0);
	//char *buf = new(std::nothrow) char[1024];
	//recv(client_fd, buf, 1024);
        printf("accept client ip: %s : %s\n", inet_ntoa(addr_client.sin_addr), buf);
        send(client_fd, "ok", 2, 0);
        close(client_fd);
	//delete buf;
	//buf = null;
    }
    close(sock_fd);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值