Linux下C语言TCP编程01

来源:互联网 发布:阿里云研究报告 编辑:程序博客网 时间:2024/05/18 05:34
#include <stdio.h>#include <stdlib.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <sys/types.h> #include <string.h>int main(){int ret = 0;    int addr_len;int portnum = 5555;//端口号char recvbuffer[1024];//接收数据缓冲区    int serversocket,clientsocket;//服务器调用socket()之后的描述符    struct sockaddr_in server_addr;//服务器套接字    struct sockaddr_in client_addr;//客户端套接字    /*-------------------------------结构体sockaddr_in的注释----------------------------    头文件:#include<netinet/in.h>    struct sockaddr_in    {        short sin_family;//Address family一般来说AF_INET(地址族)PF_INET(协议族)        unsigned short sin_port;//Port number(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)        struct in_addr sin_addr;//IP address in network byte order(Internet address)        unsigned char sin_zero[8];//Same size as struct sockaddr没有实际意义,只是为了跟SOCKADDR结构在内存中对齐;SOCKADDR结构体和该结构体意义一样,16个字节长度。    };    -----------------------------------------------------------------------------------*/    serversocket = socket(AF_INET,SOCK_STREAM,0);    /*----------------------------------socket()注释------------------------------------        socket()函数用于根据指定的地址族、数据类型和协议来分配一个套接口的描述字及其所用的资源。    #include <sys/socket.h>    int socket( int af, int type, int protocol);        af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。        type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。            常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。        protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。            常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,            它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。            如果协议protocol未指定(等于0),则使用缺省的连接方式。    ---------------------------------------------------------------------------------*/    if(serversocket == -1)    {        perror("socket() failed!");//头文件#include<stdio.h>,打印上一步操作的错误信息        return -1; //main()函数返回-1,会导致程序退出。        //return和exit的差别就是前者是返回一个值给函数,退出该函数,而后者是属于系统级别的,将返回值给系统,整个进程退出。    }        //初始化服务器套接字,就是将该套接字内容置0;    bzero(&server_addr,sizeof(server_addr));    /* 头文件#include <string.h>    void bzero(void *s, int n);会将参数s所指的内存区域前n个字节,全部设为零值。*/        //填充服务器套接字server_addr    server_addr.sin_family = AF_INET;    server_addr.sin_port = htons(portnum);//头文件:#include <arpa/inet.h>,    //必须要采用网络数据格式-大端字节序,普通数字可以用htons()函数转换成网络数据格式的数字        server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//设置服务器套接字的IP地址为特殊值INADDR_ANY,这表示服务器愿意接收来自任何网络设备接口的客户机连接。htonl()函数的意思是将主机顺序的字节转换成网络顺序的字节。    /*sin_addr类型是结构体:struct in_addr {in_addr_t s_addr;}; * 其中in_addr_t 一般为 32位的unsigned long. * 其中每8位代表一个IP地址位中的一个数值. * 例如192.168.3.144记为0xc0a80390,其中 c0 为192 ,a8 为 168, 03 为 3 , 90 为 144; * 打印的时候可以调用inet_ntoa()函数将其转换为char *类型. *///注释/*绑定套接字,使用函数:   * int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);     * 其头文件为:#include <sys/types.h>和#include <sys/socket.h> * */     if(bind(serversocket,(struct sockaddr *)&server_addr,sizeof(server_addr))==-1)     {         perror("bind() failed!");         return -1;     }     /*开始监听    int listen( int sockfd, int backlog);头文件#include <sys/socket.h>    sockfd:用于标识一个已捆绑未连接套接口的描述字。    backlog:等待连接队列的最大长度。*/    if(listen(serversocket,10))    {         perror("listen() failed!");        return -1;    }        /*等待 客户端连接    #include <sys/types.h>             #include <sys/socket.h>    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);        //调用accept函数后,会进入阻塞状态        //accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,        //serverSocket和client_addr。        //serverSocket仍然继续在监听状态,client则负责接收和发送数据        //clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号        //addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。        //传出的是客户端地址结构体的实际长度,因此此处必须定义一个addr_len变量        //出错返回-1*/    while(1){printf("服务器等待连接请求......\n");addr_len = sizeof(client_addr);        clientsocket = accept(serversocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);        if(clientsocket == -1)        {             perror("accept() failed!");            return -1;        }            printf("client IP:%s\t Port:%d\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));        //inet_ntoa   ip地址转换函数,将网络字节序IP转换为点分十进制IP        //表达式:char *inet_ntoa (struct in_addr);    while(1){ //接受消息,特别注意第三个参数一定要小于recvbuffer的实际长度。bzero(recvbuffer,sizeof(recvbuffer));ret = recv(clientsocket,recvbuffer,sizeof(recvbuffer)-1,0);if(ret < 0 ){perror("recv() failed!"); continue; }if(ret == 0 ){printf("recv() is null,客户端已经关闭,继续监听\n");close(clientsocket); break; }recvbuffer[ret]='\0';//如果上面recv()的第三个参数要是等于recvbuffer的长度,//返回的ret也有可能等于这个长度,就会导致此处末尾加'\0出错'printf("收到的消息长度:%d\n内容:%s\n",ret,recvbuffer);if(strcmp(recvbuffer,"quit") == 0){ while(1){printf("客户端:%s停止访问!\n服务器关闭,输入yes\t继续监听,输入no\n请输入你的选择(yes/no):",inet_ntoa(client_addr.sin_addr));char *qstr = (char *)malloc(10*sizeof(char));scanf("%s",qstr);if(strcmp(qstr,"yes") == 0){printf("服务器停止监听,关闭退出!\n");close(clientsocket);close(serversocket);exit(0);}else if(strcmp(qstr,"no") == 0){printf("服务器继续监听!\n");close(clientsocket);break;}else{printf("输入错误,请重新输入!\n");} } break;} send(clientsocket,recvbuffer,ret,0);}}return 0;}

原创粉丝点击