Linux TCP编程常见问题考虑

1 搭建一个TCP服务器

源码:tcp_server.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXBYTES 1024
#define SERVER_PORT 8888
int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, clifd;
    char str[INET_ADDRSTRLEN];
    char buf[MAXBYTES];
    int i, n, len;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 128);
    printf("Accepting connections ...\n");
    while (1) 
    {
        bzero(&buf, sizeof(buf));
        cliaddr_len = sizeof(cliaddr);
        clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
        if(clifd == -1)
        {
            perror("clifd");
            continue;
        }
        len = read(clifd, buf, sizeof(buf) - 1);
        if(len != -1)
        {
            printf("accept from %s at PORT %d :[%d] %s\n"
                , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str))
                , ntohs(cliaddr.sin_port), len, buf);
        }
        else
        {
            perror("read");
        }
        close(clifd);
    }
    return 0;

测试服务器是否好用可以使用一下命令:

nc 127.0.0.1 8888

2 搭建一个TCP客户端

源码:tcp_client.c

#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define SERVER_PORT 8888
int main(int argc, char *argv[])
{
    struct sockaddr_in serveraddr;
    int sockfd, n, ret;
    char buf[] = "helloworld";
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr);
    serveraddr.sin_port = htons(SERVER_PORT);
    ret = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
    if(ret == -1)
      perror("connect");
  
    ret = write(sockfd, buf, strlen(buf));
    if(ret == -1)
      perror("connect");
    close(sockfd);
    return 0;
}

需要注意的事:

​ 1.当socket写端关闭的时候,读端read将一直读取到0字节,可以使用这一特性来关闭读端套接字。

​ 2.当socket读端关闭的时候,写端write继续写入数据,将会报错,并且收到一个SIGPIPE信号。

​ 3.当网络异常的时候(如拔掉网线),这个时候read不会返回错误,将阻塞等待数据;write也不会返回错误,仍然可以写入数据,直到写buffer填满,将阻塞write函数。

​ 4.socket有个buffer,并且这个buffer的大小是一定的,当一直往socket write的时候,达到这个buffer的最大值后,write将阻塞。

服务器改写如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#define MAXBYTES 1024
#define SERVER_PORT 8888
int main(void)
{
    struct sockaddr_in servaddr, cliaddr;
    socklen_t cliaddr_len;
    int listenfd, clifd;
    char str[INET_ADDRSTRLEN];
    char buf[MAXBYTES];
    int i, n, len;
  
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);
    bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    listen(listenfd, 128);
    printf("Accepting connections ...\n");
    while (1) 
    {
        bzero(&buf, sizeof(buf));
        cliaddr_len = sizeof(cliaddr);
        clifd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //可能被信号中断需要注意
        if(clifd == -1)
        {
            perror("read");
            return -1;
        }   
        while(1)
        {
            len = read(clifd, buf, sizeof(buf) - 1);
            if(len > 0)
            {
                printf("accept from %s at PORT %d :[%d] %s\n"
                        , inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str))
                        , ntohs(cliaddr.sin_port), len, buf);
            }
            else if(len == 0) //写端关闭的时候将永远读到0字节,如果读到0字节就可以关闭套接字了
            {
                close(clifd);
                break;
            }
            else
            {
                perror("read");
            }
            sleep(1);
        }
        
    }   
    close(listenfd);
    return 0;

}

客户端改写如下:

#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#define SERVER_PORT 8888
void sig_handler(int sig)
{
    printf("SIGPIPE\n");
}

int main(int argc, char *argv[])
{
    signal(SIGPIPE, sig_handler); //当读端关闭的时候,如果继续写,则会触发SIGPIPE信号
    struct sockaddr_in serveraddr;
    int sockfd, n, ret; 
    char buf[] = "helloworld";
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET, "127.0.0.1", &serveraddr.sin_addr);
    serveraddr.sin_port = htons(SERVER_PORT);

    int conntimes = 0;
    while(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1) //有可能链接失败,通过这种方式定时链接
    {
        if(conntimes++ > 10)
          return -1;
        sleep(1);
        perror("connect");
        
    }

    int num = 0;
    while(1)
    {
        ret = write(sockfd, buf, strlen(buf));
        if(ret == -1)
        {
            perror("write");
        }
        else
            num += ret;
        printf("send bytes %d\n", num);
        sleep(1); //该值根据需要进行更改。
    }
    close(sockfd);
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值