思维导图:
模拟面试题:
1.怎么修改文件描述符发标志位?
答:用fcntl函数取下旧文件标识位,在此基础上加上O_NINBLOCK属性,然后设置回去
2.udp本地通信需要注意哪些方面?
答:如果没有绑定套接字文件,服务器无法对客户端发送信息,客户端可以对服务器发送信息。
3.基于udp的聊天室如何实现数据群发?
答:在服务器端创建链表,存储客户端的信息,当有信息需要发送时遍历链表,发送指定客户端的信息。
4.基于udp的聊天室如何实现数据群发?
答:使用多进程,多线程或IO多路复用,poll多路复用。
5.TCP连接时的三次握手机制?
答:需要连接服务器与客户端时,客户端向服务器发送连接请求;服务器收到后向客户端发送已收到连接请求,发起连接请求;客户端收到连接请求后给出应答。
作业:
1.
1 #include
2 #define SER_PORT 8888 //服务器端口号
3 #define SER_IP "192.168.122.122" //服务器ip地址
4 #define CLI_PORT 6666 //客户端的端口号
5 #define CLI_IP "192.168.122.144" //客户端ip地址
6
7
8 int main(int argc, const char *argv[])
9 {
10 //1、创建用于通信的套接字文件描述符
11 int cfd = -1;
12 cfd = socket(AF_INET, SOCK_STREAM, 0);
13 if(cfd == -1)
14 {
15 perror("socket error");
16 return -1;
17 }
18 printf("cfd = %d\n", cfd); //3
19
20 //2、绑定(可选)
21 //2.1 填充地址信息结构体
22 struct sockaddr_in cin;
23 cin.sin_family = AF_INET;
24 cin.sin_port = htons(CLI_PORT);
25 cin.sin_addr.s_addr = inet_addr(CLI_IP);
26 //2.2绑定
27 if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
28 {
29 perror("bind error");
30 return -1;
31 }
32 printf("bind success\n");
33
34 //3、连接服务器
35 //3.1 填充服务器地址信息结构体
36 struct sockaddr_in sin;
37 sin.sin_family = AF_INET;
38 sin.sin_port = htons(SER_PORT);
39 sin.sin_addr.s_addr = inet_addr(SER_IP);
40 //3.2 连接
41 if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
42 {
43 perror("connect error");
44 return -1;
45 }
46 printf("connect success\n");
47
48 fd_set readfds;
49 FD_ZERO(&readfds);
50 FD_SET(0,&readfds);
51 FD_SET(cfd,&readfds);
52
53 //4、收发数据
54 char buf[128] = "";
55 while(1)
56 {
57 //清空数组
58 bzero(buf, sizeof(buf));
59
60 printf("请输入>>>");
61 int res=select(cfd+1, &readfds, NULL, NULL, NULL);
62 if(res==-1)
63 {
64 perror("select error");
65 return -1;
66 }
67 else if(res==0)
68 {
69 printf("timeout\n");
70 return -1;
71 }
72
73 fgets(buf, sizeof(buf), stdin); //从终端输入数据
74 buf[strlen(buf)-1] = 0; //将换行改为'\0'
75
76 //发送给服务器
77 send(cfd, buf, sizeof(buf), 0);
78
79 printf("发送成功\n");
80 if(strcmp(buf, "quit") == 0)
81 {
82 break;
83 }
84
85 //接收服务器发来的消息
86 recv(cfd, buf, sizeof(buf), 0);
87 printf("[%s:%d]:%s\n", SER_IP, SER_PORT, buf);
88 }
89
90 //5、关闭套接字
91 close(cfd);
92
93 return 0;
94 }
95
~
2.
1 #include
2 #define SER_PORT 8888 //服务器端口号
3 #define SER_IP "192.168.122.144" //服务器IP地址
4
5
6 int main(int argc, const char *argv[])
7 {
8 //1、创建用于连接的套接字
9 int sfd = socket(AF_INET, SOCK_STREAM, 0);
10 //参数1:通信域,表明使用的是ipv4协议
11 //参数2:通信方式,使用TCP通信
12 //参数3:0表示之前已经指定协议 IPPROTO_TCP
13
14 if(sfd == -1)
15 {
16 perror("socket error");
17 return -1;
18 }
19 printf("sfd = %d\n", sfd); //3
20
21
22 //将端口号快速重用函数
23 int reuse = 1;
24 if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
25 {
26 perror("setsockopt error");
27 return -1;
28 }
29 printf("端口号快速重用成功\n");
30
31
32
33 //2、给当前套接字绑定IP地址和端口号
34 //2.1填充要绑定的地址信息结构体
35 struct sockaddr_in sin;
36 sin.sin_family = AF_INET; //通信域
37 sin.sin_port = htons(SER_PORT); //端口号
38 sin.sin_addr.s_addr = inet_addr(SER_IP); //ip地址
39
40 //2.2 绑定
41 if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
42 {
43 perror("bind error");
44 return -1;
45 }
46 printf("bind success %s %s %d\n", __FILE__, __func__, __LINE__);
47
48 //3、将套接字设置成监听状态
49 if(listen(sfd, 128) == -1)
50 {
51 perror("listen error");
52 return -1;
53 }
54 printf("listen success %s %s %d\n", __FILE__, __func__, __LINE__);
55
56 //4、阻塞等待客户端的链接请求
57 //4.1定义容器接收客户端的地址信息
58 struct sockaddr_in cin; //用于接收地址信息
59 socklen_t socklen = sizeof(cin); //用于接收地址信息的大小
60
61 int newfd = -1;
62
63
64 //11、定义一个等待文件描述符结构体数组
65 struct pollfd pfd[2];
66
67 //22、填充要等待的文件描述符及事件
68 pfd[0].fd = 0; //将0号文件描述符放入检测集合中
69 pfd[0].events = POLLIN; //表示检测该文件描述符的读事件
70
71 pfd[1].fd = cfd; //将cfd文件描述符放入检测集合中
72 pfd[1].events = POLLIN; //表示检测该文件描述符的读事件
73
74
75 //定义一个地址信息结构体数组,每一个元素对应一个客户端文件描述符
76 struct sockaddr_in cin_arr[1024];
77
78
79
80 while(1)
81 {
82 //判断是否是文件描述符触发事件
83 int res = poll(pfd,2,-1);
84 if(res == -1)
85 {
86 perror("poll error");
87 return -1;
88 }else if(res == 0)
89 {
90 printf("timeout\n");
91 return -1;
92 }
93
94 //程序执行至此,说明已经有事件产生并且解除了select的阻塞
95 //判断哪个文件描述符还在集合中,如果在,就执行相关函数
96 //4.2 接收客户端的链接
97 newfd = accept(sfd, (struct sockaddr*)&cin, &socklen);
98 if(newfd == -1)
99 {
100 perror("accept error");
101 return -1;
102 }
103 printf("[%s:%d]发来链接请求 %s %s %d\n", \
104 inet_ntoa(cin.sin_addr), ntohs(cin.sin_port),__FILE__, __func__, __LINE__);
105
106 //更新地址信息结构体数组
107 cin_arr[newfd] = cin;
108
109 //终端输入
110 char wbuf[128] = "";
111 scanf("%s", wbuf);
112 printf("触发了终端输入事件。。。\n");
113 if(strcmp(wbuf, "quit") == 0)
114 {
115 break;
116 }
117
118 //将消息发送给所有客户端
119 for(int i=4; i<=maxfd; i++ )
120 {
121 sendto(i, wbuf, sizeof(wbuf), 0,(struct sockaddr*) &cin_arr[i], sizeof(cin_arr[i]));
122 }
123
124 //5、跟客户端进行消息通信
125 char buf[128] = "";
126
127 //将数组清空
128 bzero(buf, sizeof(buf));
129
130 //读取客户端发来的消息
131 //int res = read(newfd, buf, sizeof(buf));
132 int res = recv(cli, buf, sizeof(buf), 0);
133 if(res == 0)
134 {
135 printf("客户端已经下线\n");
136 //关闭当前通信的套接字
137 close(cli);
138 //将当前文件描述符从文件描述符集合中移除
139 FD_CLR(cli, &readfds);
140
141 //更新maxfd
142 for(int i=maxfd; i>=sfd; i--)
143 {
144 if(FD_ISSET(i, &readfds))
145 {
146 maxfd = i;
147 break;
148 }
149 }
150
151
152
153 continue;
154 }
155 printf("[%s:%d] : %s\n", inet_ntoa(cin_arr[cli].sin_addr), ntohs(cin_arr[cli].sin_port), buf);
156
157 //给客户端发消息
158 strcat(buf, "*_*");
159
160 //write(newfd, buf, sizeof(buf));
161 send(cli, buf, sizeof(buf), 0);
162 printf("发送成功\n");
163
164 }
165 //6、关闭套接字
166 close(sfd);
167
168
169
170
171
172 return 0;
173 }
174
~
~