Linux下C语言TCP编程02

来源:互联网 发布:vscode 支持xp吗 编辑:程序博客网 时间:2024/05/17 07:46
#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>#include <unistd.h>extern char **environ;int clientNum=0;char clientName[7]="client";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*/printf("服务器等待连接请求......\n");    //system("gnome-terminal -e ./client pause");/*底下这两行是在启动server的时候,启动当前目录下的client,如果没有客户端,或者客户端不是这个名称,请屏蔽这两行代码*/if(fork()==0)execl("/usr/bin/gnome-terminal","./client pause",NULL);while(1){addr_len = sizeof(client_addr);        clientsocket = accept(serversocket, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);        if(clientsocket == -1)        {              perror("accept() failed!");            return -1;        }clientNum++;//客户端个数=子进程编号pid_t cfd;cfd = fork();if(cfd < 0 ){perror("fork():");} else if(cfd == 0){char *numStr;numStr = (char *)malloc(sizeof(char)*3);sprintf(numStr,"%d",clientNum);strcat(clientName,numStr);        printf("新的客户端:%s pid:%d IP:%s Port:%d\n",clientName,getpid(),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("%s已经关闭,服务器继续监听\n");close(clientsocket); break; }recvbuffer[ret]='\0';//如果上面recv()的第三个参数要是等于recvbuffer的长度,//返回的ret也有可能等于这个长度,就会导致此处末尾加'\0出错'printf("收到%s的消息长度:%d 内容:%s\n",clientName,ret,recvbuffer);if(strcmp(recvbuffer,"quit") == 0){ printf("%s:%s停止访问!服务器继续监听\n",clientName,inet_ntoa(client_addr.sin_addr));close(clientsocket); break;}   send(clientsocket,recvbuffer,ret,0);} } }return 0;}