首页 > 代码库 > Socket编程-并发服务器为例

Socket编程-并发服务器为例

直接上代码,内置注解

1.server端

  1 /**  2     server端  3 */  4 #include <stdio.h>  5 #include <stdlib.h>  6 #include <string.h>  7 #include <unistd.h>  8 #include <sys/types.h>  9 #include <sys/socket.h> 10 #include <netinet/in.h> 11 #include <arpa/inet.h> 12  13 #define PORT 1234  //监听端口 14 #define BACKLOG 5 15 #define MAXDATASIZE 1000  //数据包的大小 16  17 void process_cli(int  connfd, struct sockaddr_in client); 18  19 int main() 20 { 21     int  listenfd, connfd; 22     pid_t  pid; 23     struct  sockaddr_in  server; 24     struct sockaddr_in  client; 25     int  len; 26     /** 27     生成一个TCP报文,PF_INET,AF_INET:ipv4网络协议,PF_INET6,AF_INET6:ipv6网络协议 28     SOCK_STREAM提供面向连接的稳定数据传输,即TCP协议 29     SOCK_STREAM: 提供面向连接的稳定数据传输,即TCP协议。 30   OOB: 在所有数据传送前必须使用connect()来建立连接状态。 31   SOCK_DGRAM: 使用不连续不可靠的数据包连接。 32   SOCK_SEQPACKET: 提供连续可靠的数据包连接。 33   SOCK_RAW: 提供原始网络协议存取。 34   SOCK_RDM: 提供可靠的数据包连接。 35   SOCK_PACKET: 与网络驱动程序直接通信 36     **/ 37     if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) { 38         perror("Creating socket failed."); 39         exit(1); 40     } 41  42     int opt =SO_REUSEADDR; 43     /** 44     绑定在本地IP上 127.0.0.1 45     SO_REUSEADDE 在服务器重启后,在相同的本地接口以端后上进行监听 46     **/ 47     setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 48     /** 49     bzero是将数据置零,其中前一个是置零的起始地址,后一个置零的个数 50     推荐使用memset代替 bzero 51     **/ 52     bzero(&server,sizeof(server)); 53     //设置套接字的地址 54     server.sin_family=AF_INET; 55     server.sin_port=htons(PORT); 56     server.sin_addr.s_addr= htonl (INADDR_ANY); 57     /** 58     该函数指明套接字将使用本地的哪一个协议端口进行数据传送(IP地址和端口号) 59     中间的参数是通用地址,0表示成功,-1表示出错 60     **/ 61     if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) { 62         perror("Bind()error."); 63         exit(1); 64     } 65     /** 66     函数listen仅被服务器调用,它完成两件事: 67         1)函数listen将未连接的套接字转化成被动套接字,指示内核应接受指向此套接字的连接请求 68         2)函数的第二参数规定了内核为此套接字排队的最大连接个数 69     对于给定的监听套接字,内核要维护的两个队列 70         1)未完成连接的队列 71         2)已完成连接的队列 72         3)两者之和不能超过backlog 73     */ 74     if(listen(listenfd,BACKLOG)== -1){ 75         perror("listen() error\n"); 76         exit(1); 77     } 78     len=sizeof(client); 79  80     while(1) 81     { 82         /** 83         accept函数由TCP服务器调用,从已完成连接队列头返回下一个已完成连接; 84         如果该队列为空,则进程进入睡眠状态 85         函数返回的套接字为已连接套接字,应与监听套接字区分开 86         该函数最多返回三个值,一个既可能是新套接字也可能是错误指示的整数,一个客户 87         进程的协议地址(由cliaddr所指),以及该地址的大小(这后两个参数是值-结果参数) 88         也就是说,服务器可以通过参数cliaddr来得到请求连接并获得成功的客户的地址和端口号 89         */ 90     if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) { 91         perror("accept() error\n"); 92         exit(1); 93     } 94     if ((pid=fork())>0){ 95         close(connfd); 96         continue; 97     }else if (pid==0) { 98         close(listenfd); 99         process_cli(connfd, client);100         exit(0);101     }else {102         printf("fork()error\n");103         exit(0);104     }105 }106     close(listenfd);107 }108 109 void process_cli(int connfd, struct sockaddr_in client)110 {111     int num;112     char  recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];113     printf("Yougot a connection from %s. ",inet_ntoa(client.sin_addr) );114     /**115      返回大于0表示成功接收的数据长度,0表示对方已关闭,-1出错116      最后一个参数:117         0:常规操作,如同read()函数118         MSG_PEEK: 只查看数据而不读出数据,后序读操作仍能读出所查看的该数据119         MSG_OOB:忽略常规数据,而只读带外数据120         MSG_WAITALL: recv 函数只有在将接收缓冲区填满后才返回121     **/122     num = recv(connfd,cli_name, MAXDATASIZE,0);123     if (num == 0)124     {125         close(connfd);126         printf("Client disconnected.\n");127         return;128     }129     cli_name[num - 1] =\0;130     printf("Client‘sname is %s.\n",cli_name);131 132     while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {133         recvbuf[num] =\0;134         printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);135         int i = 0;136     for (i = 0;i < num - 1; i++) {137         if((recvbuf[i]>=a&&recvbuf[i]<=z)||(recvbuf[i]>=A&&recvbuf[i]<=Z))138     {139         recvbuf[i]=recvbuf[i]+ 3;140         if((recvbuf[i]>Z&&recvbuf[i]<=Z+3)||(recvbuf[i]>z))141         recvbuf[i]=recvbuf[i]- 26;142     }143         sendbuf[i] =recvbuf[i];144     }145         sendbuf[num - 1]= \0;146         /**147             返回:非0发送成功的数据长度,-1出错148             最后一个参数:149             0:常规操作,如同write()函数150             MSG_OOB,发送带外数据(TCP紧急数据)151             MSG_DONTROUTE:忽略底层协议的路由设置,只能将数据发送给与发送机处于在同一个网络152             中的机器上153         */154         send(connfd,sendbuf,strlen(sendbuf),0);155     }156         close(connfd);157 }
View Code

2.client端

 1 /** 2     client端 3 **/ 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <sys/types.h> 9 #include <sys/socket.h>10 #include <netinet/in.h>11 #include <netdb.h>12 13 #define PORT 123414 #define MAXDATASIZE 10015 void process(FILE*fp, int sockfd);16 char *getMessage(char* sendline,int len, FILE* fp);17 18 int main(int argc,char *argv[])19 {20     int fd;21     struct hostent  *he;22     struct sockaddr_in  server;23     //接收IP地址24     if (argc !=2) {25         printf("Usage:%s <IP Address>\n",argv[0]);26         exit(1);27     }28     //域名解析函数,注意argv[1]才是,传入的参数29     if((he=gethostbyname(argv[1]))==NULL){30         printf("gethostbyname() error\n");31         exit(1);32     }33     if((fd=socket(AF_INET, SOCK_STREAM, 0))==-1){34         printf("socket()error\n");35         exit(1);36     }37 38     bzero(&server,sizeof(server));39     server.sin_family =AF_INET;40     server.sin_port=htons(PORT);41     server.sin_addr= *((struct in_addr *)he->h_addr);42     /**43     函数connect激发TCP的三路握手过程,仅在成功或出错返回,44     错误有以下几种情况:45         如果客户没有收到SYN分节的响应(总共75秒,之间需要可能需要重发若干次SYN)46         则返回ETIMEDOUT47         如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程,在等待与之相连,48         函数返回错误ECONNREFUSED49         如果客户番薯的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户上的内核保存此消息,50         并按第一种情况,连续发送SYN,直到规定时间,返回保存的消息(即ICMP错误),作为EHOSTUNREACH51         或ENETUNREACH错误返回给进程52     **/53     if(connect(fd,(struct sockaddr *)&server,sizeof(server))==-1){54         printf("connect() error\n");55         exit(1);56     }57 58     process(stdin,fd);59 60     close(fd);61 }62 63 void process(FILE *fp, int  sockfd)64 {65     char sendline[MAXDATASIZE],recvline[MAXDATASIZE];66     int num;67 68     printf("Connected to server. \n");69     printf("Input client‘s name : ");70     //从fp指向的文件读取一个长度为num-1的字符串,存入起始地址为buf的空间,返回地址buf71     //若遇到文件结束或出错,返回NULL,其中fp是从标准输入读取数据72     if (fgets(sendline, MAXDATASIZE, fp) == NULL) {73         printf("\nExit.\n");74         return;75     }76     send(sockfd,sendline, strlen(sendline),0);77     while(getMessage(sendline, MAXDATASIZE, fp) != NULL) {78     send(sockfd,sendline, strlen(sendline),0);79 80     if ((num =recv(sockfd, recvline, MAXDATASIZE,0)) == 0) {81         printf("Server terminated.\n");82         return;83     }84 85     recvline[num]=\0;86     printf("Server Message: %s\n",recvline);87 88 }89     printf("\nExit.\n");90 }91 92 char  *getMessage(char*  sendline,int len, FILE*  fp)93 {94     printf("Inputstring to server:");95     return(fgets(sendline,MAXDATASIZE, fp));96 }
View Code

参考

1:参考一

2:参考二

Socket编程-并发服务器为例