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 1234
14 #define MAXDATASIZE 100
15 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         则返回ETIMEDOUT
47         如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程,在等待与之相连,
48         函数返回错误ECONNREFUSED
49         如果客户番薯的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户上的内核保存此消息,
50         并按第一种情况,连续发送SYN,直到规定时间,返回保存的消息(即ICMP错误),作为EHOSTUNREACH
51         或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的空间,返回地址buf
71     //若遇到文件结束或出错,返回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:参考二

时间: 2024-10-26 05:47:48

Socket编程-并发服务器为例的相关文章

Linux 网络编程——并发服务器的三种实现模型

服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多对一),为了处理客户的请求,对服务端的程序就提出了特殊的要求. 目前最常用的服务器模型有: ·循环服务器:服务器在同一时刻只能响应一个客户端的请求 ·并发服务器:服务器在同一时刻可以响应多个客户端的请求 UDP 循环服务器的实现方法 UDP 循环服务器每次从套接字上读取一个客户端的请求 -> 处理

Socket编程--并发server

Socket地址复用 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项.该选

[socket编程] 一个服务器与多个客户端之间通信

转自:http://blog.csdn.net/neicole/article/details/7539444 并加以改进 Server程序: 1 // OneServerMain.cpp 2 3 #include <iostream> 4 #include <cstdio> 5 #include <string> 6 #include <cstring> 7 #include <vector> 8 #include <iterator&g

TCP socket 多线程 并发服务器(发送)与客户端(接收)

实现功能:Ubuntu上通过多线程实现服务器并发给客户端发送文件,携带包头,根据包头信息命名新文件.适用于短连接. 问题小结: 01. 调用嵌套在结构体中的结构体char 数组成员时,需要动态分配内存. 02. 使用select() 监听socket变化.select() 其中三个参数分别监听读,写,出错. 03. 每条线程在同时发送文件时,需要使用独立的变量,如accept(), FILE *fd, *buff 等,用结构数组 + 标号实现. 04. struct stat stat_buff

socket编程之服务器和客户端

服务端和客户端就像银行出纳员和客户样,一个银行出纳员(服务器),不吃不睡的服务排队的客户,每个客户都会有要解决的问题,一旦完成,客户就会走开,出纳员等待下一个客户的到来. 服务器端: from socket import * from time import ctime HOST = '' #空白,是对bind方法的标识,可以使用任何可用的地址 PORT = 21567 BUFSIZ = 1024 #缓冲区大小设为1kb ADDR = (HOST, PORT) tcpSerSock = sock

【windows socket编程+服务器客户端】

Windows Socket编程与服务器客户端示例 Winsock是 Windows下套接字标准. Socket套接字基于计算机网络,提供同一系统上不同进程或由局域网连接在一起的不同机器上的进程间通讯功能.如下图: 套接字通过IP地址,Port端口号标识,通过这个标识可以在整个局域网定位一个套接字,通过套接字进程便可以相互传输数据.如:进程A与进程B之间欲通过套接字通信,首先进程A创建一个有IP地址,端口号唯一标识的套接字,进程B同样创建一个有IP地址,端口号唯一标识的套接字,进程A,B便可以通

Java学习之Socket编程

什么是Socket Java中的Socket编程其实就是网络编程,一般使用基于TCP/IP协议的Socket编程.所有关于Socket编程的API都在Java.net包里,一般实现客户端和服务器端之间的通讯. Socket通讯的过程 Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息.一个连接就建立起来了.Server端和Client 端都可以通过Send,Write

socket编程之并发回射服务器3

在socket编程之并发回射服务器一文中,服务器采用多进程的方式实现并发,本文采用多线程的方式实现并发. 多线程相关API: // Compile and link with -pthread int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int pthread_join(pthread_t thread, void **

Java Socket编程(四) 重复和并发服务器

重复和并发服务器 这个应用程序被当作一个重复的服务器.因为它只有在处理完一个进程以后才会接受另一个连接.更多的复杂服务器是并发的.它为每一个请求分配一个线程,而不是来一个处理一个.所以看起来它在同时处理多人请求.所有的商业的服务器都是并发的服务器. Java数据报类 不像面向连接的类,数据报的客户端和服务器端的类在表面上是一样的.下面的程序建立了一个客户和服务器商的数据报sockets: DatagramSocket serverSocket = new DatagramSocket( 4545