首页 > 代码库 > 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 }
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 }
参考
1:参考一
2:参考二
Socket编程-并发服务器为例
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。