Linux--网络编程(三)TCP编程

76 篇文章 5 订阅

数据流通信

 数据流套接口是可靠的面向连接的通信数据流。如果套接口中以“1,2”的顺序放入两数据,它们在另一端也会以“1,2”的顺序到达,它们也可以被认为是无错误的传输。


TCP编程流程

这里写图片描述


函数介绍

socket()

 使用系统调用socket()来获取文件描述符,该调用的声明格式如下:

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
socket函数说明

这里写图片描述


bind()

 一旦有了一个套接口以后,下一步工作就是把套接口绑定到本地计算机的某个端口上,但如果只想使用connect()则无此必要。其定义如下:

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
bind函数说明

这里写图片描述


connect()

 connect()系统调用主要是由客户端完成的,其定义如下:

#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
connect函数说明

这里写图片描述


listen()

 在服务器端,如果希望等待一个进入的连接请求,然后再处理这个连接请求,可以通过首先调用listen(),然后再调用accept()来实现。系统调用定义为:

#include <sys/socket.h>
int listen(int sockfd, int backlog);
listen函数说明

这里写图片描述


accept()

 当在远程端的客户机试图通过connect()连接服务器使用listen()正在侦听的端口时,此连接将会在队列中等待,直到服务器使用accept()处理它。调用accept()之后,将会返回一个全新的套接口文件描述符来处理这个连接。其定义如下:

#include <sys/socket.h>
int accept(int sockfd, void *addr, int *addrlen);
accept函数说明

这里写图片描述


send()和recv()

 这两个函数是在建立连接后用于完成发送与接收数据的系统调用,它们的用法为:

#include <sys/socket.h>
int sent(int sockfd, const void *msg, int len, int flags);
int recv(int sockfd, void *buf, int len, unsigned int flags);
send和recv函数说明

这里写图片描述


sendto()和recvfrom()

 因为数据报套接字是无连接的,它并不连接到远程的主机上,所以在发送数据包之前,必须首先给出目的地址。其定义如下:

int sendto(int sockfd, const void *msg, int len, unsigned int flags,const struct sockaddr *to, int tolen);
int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen);
sendto和recvfrom函数说明

这里写图片描述


close()和shutdown()

 可以使用close()调用关闭连接的套接口文件描述符,它的调用方式为:

chose(sockfd);
 这样以后就不能再对此套接口进行任何读/写操作了。使用系统调用shutdown(),可有更多的控制权。
int shutdown(int sockfd, int how);

  • 第一个参数是希望切断通信的套接口文件描述符。第2个how的值如下:
    • 0:不允许再接收数据
    • 1:不允许再发送数据
    • 2:发送和接收数据都不允许
  • shutdown()调用如果成功返回0,如果失败返回-1;

附加知识

 字符串的IP地址和32位的IP地址的转换
“192.168.0.1”应该表示为C0A80001

  • int inet_aton(const char *cp, struct in_addr *inp)
  • char *inet_ntoz(struct in_addr in)
  • 其中,a 代表“ASCII”,n 代表“network”
  • 第一个函数表示将a.b.c.d的IP转换为32位的IP地址,存储在inp指针里面
  • 第二个函数是指将32位IP地址转换为“a.b.c.d”的字符串格式

————————————————————-

编程

服务器程序版本一:极客学院版

/*******************服务器端程序*****************/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <time.h>

int sockfd;

void do_service(fd)/*返回一个时间给客户端*/
{
    long t = time(0);
    char *s = ctime(&t);
    write(fd,s,strlen(s));
}
void out_client(struct sockaddr_in clientaddr)/*在服务器端显示客户端的IP地址和端口号*/
{
    char buffer[16];
    inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,buffer,sizeof(clientaddr));
    unsigned short port = ntohs(clientaddr.sin_port);
    printf("client ip:%s (%d)\n",buffer,port);
}


int main(int argc,char *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr,"usage: %s port\n",argv[0]);
        exit(1);
    }

    /*create socket*/
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        fprintf(stderr,"socket: %s\n",strerror(errno));
        exit(1);
    }

    /*set ip and port*/
    struct sockaddr_in addr;
    memset(&addr,0,sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[1]));
    addr.sin_addr.s_addr = INADDR_ANY; 

    /*bind ip and port*/
    int len = sizeof(addr);
    if(bind(sockfd,(struct sockaddr*)&addr,len) < 0)
    {
        fprintf(stderr,"bind: %s\n",strerror(errno));
        exit(1);
    }

    /*begin to listen*/
    if(listen(sockfd,10) < 0)
    {
        fprintf(stderr,"listen: %s\n",strerror(errno));
        exit(1);
    }

    while(1)
    {
        struct sockaddr_in clientaddr;
        int c_len = sizeof(clientaddr);
        int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&c_len);
        if(fd < 0)
        {
            fprintf(stderr,"accept: %s\n",strerror(errno));
            continue;
        }
        out_client(clientaddr);
        do_service(fd);
        close(fd);

    }
    return 0;
}

服务器版本二: CDNS学院版–有段错误!

/***********服务器程序************/

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define WAITBUF 10

int main(int argc,char *argv[]){
    int sockfd,new_fd;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    int sin_size,portnumber;

    char hello[]="hello!socket communication world!\n";

    if(argc!=2){
        fprintf(stderr, "Usage:%s\n portnumber\a\n",argv[0]);
        exit(1);
    }
    /*端口号不对,退出*/
    if((portnumber = atoi(argv[1]))<0){
        fprintf(stderr, "Usage:%s\n portnumber\a\n",argv[0]);
        exit(1);
    }

    /*服务器端开始建立socket描述符*/
    /*创建流式套接字(TCP)*/
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
        fprintf(stderr, "Socket error:%s\n\a",strerror(errno));
        exit(1);
    }
    printf("Socket = %d...........\n",sockfd);

    /*服务器端填充 sockaddr结构*/
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family = AF_INET;
    /*自动填充主机IP*/
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*任意可用的端口*/
    server_addr.sin_port = htons(portnumber);

    /*捆绑sockfd描述符*/
    if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)) == -1){
        fprintf(stderr, "Bind error:%s\n\a",strerror(errno));
        exit(1);
    }
    printf("bind.................\n");

    /*监听sockfd描述符*/
    /*设置监听队列的长度*/
    if(listen(sockfd,WAITBUF) == -1){
        fprintf(stderr, "Listen error:%s\n\a",strerror(errno));
        exit(1);
    }
    printf("listen..............\n");

    while(1){/*服务器阻塞,直到客户程序建立连接*/
        sin_size = sizeof(struct sockaddr_in);
        printf("accept before....\n");
        /*accept等待客户端的连接请求,如果有请求将创建一个新的套接字用于同客户通信*/
        if((new_fd = accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size)) == -1){
            fprintf(stderr, "Accept error:%s\n\a",strerror(errno));
            exit(1);
        }
        printf("new_fd = %d\n",new_fd );/*显示第几个套接字*/
        /*可以在这里加上自己的处理函数*/

        /*通过inet_ntoa函数取出字符串形式IP地址*/
        fprintf(stderr, "Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
        if(send(new_fd,hello,strlen(hello),0) == -1){
            fprintf(stderr, "Write Error:%s\n",strerror(errno));
            exit(1);
        }
        /*这个通信已经结束*/
        close(new_fd);/*关闭分机*/
        /*循环下一个*/
    }
    close(sockfd);/*关闭总机*/
    exit(0);
}

客户端程序:

/**********客户端程序**********/

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define RECVBUFSIZE 1024

int main(int argc,char *argv[]){
    int sockfd;
    char buffer[RECVBUFSIZE];
    struct sockaddr_in server_addr;
    struct hostent *host;
    int portnumber,nbytes;

    if(argc != 3){
        fprintf(stderr, "Usage:%s hostname portnumber\a\n",argv[0]);
        exit(1);
    }

    if((portnumber=atoi(argv[2])) < 0){
        fprintf(stderr, "Usage:%s hostname portnumber\a\n",argv[0]);
        exit(1);
    }

    /*客户端程序开始建立sockfd描述符*/
    if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1){
        fprintf(stderr, "Socket Error:%s\a\n",strerror(errno));
        exit(1);
    }

    /*客户端程序填充服务端的资料*/
    bzero(&server_addr,sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(portnumber);
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);//inet_addr把字符串IP转32位IP地址

    /*客户端程序发起连接请求*/
    if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr)) == -1){
        fprintf(stderr, "Connect Error:%s\a\n",strerror(errno));
        exit(1);
    }

    /*连接成功,接收数据*/
    if((nbytes = recv(sockfd,buffer,RECVBUFSIZE,0)) == -1){
        fprintf(stderr, "Read Error:%s\a\n",strerror(errno));
        exit(1);
    }
    buffer[nbytes] = '\0';

    if(nbytes > 0)printf("I have received:%s\n",buffer);

    /*结束通信*/
    close(sockfd);
    exit(0);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值