实验过程中先关闭防火墙,避免后续出现不要问题
输入以下命令以停止防火墙服务: sudo systemctl stop iptables 输入以下命令以禁用防火墙服务: sudo systemctl disable iptables
实验验收时,应先开启server文件,再开启client文件
server.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> //中断错误提示 #define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE); } while(0) int main() { int listenfd; if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0) ERR_EXIT("socket"); struct sockaddr_in servaddr; /*memset常用于对第一个参数进行清零, 头文件:C中#include<string.h>,C++中#include<cstring> 参考http://t.csdnimg.cn/R5OR4*/ memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(10001); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) ERR_EXIT("bind"); if((listen(listenfd,SOMAXCONN)) < 0 )//主动套接字,变成被动套接字 ERR_EXIT("listen"); struct sockaddr_in peeraddr; socklen_t socklen = sizeof(peeraddr); int conn; if((conn = accept(listenfd,(struct sockaddr*)&peeraddr,&socklen)) < 0)// 获得到是主动套接字 ERR_EXIT("accept"); char recvbuf[1024] = {0};//用于接受客户端发送数据 char sendbuf[1024] = {0};//用于存储服务端发送数据 for(;;) { // 从客户端接收数据 printf("Client: %s\n", recvbuf); /*read来源头文件:#include<unistd.h> read(int fd,void*buf,size_t count) fd: 是文件描述符 buf: 为读出数据的缓冲区; count: 为每次读取的字节数(是请求读取的字节数,读上来的数据 保存在缓冲区buf中,同时文件的当前读写位置向后移)*/ read(conn,recvbuf,sizeof(recvbuf)); /*# include <stdio.h> int fputs(const char *s, FILE *stream); s 代表要输出的字符串的首地址,可以是字符数组名或字符指针变量名。 stream 表示向何种流中输出,可以是标准输出流 stdout,也可以是文件流。标准输出流即屏幕输 出,printf 其实也是向标准输出流中输出的。*/ //与上文printf函数大致相同道理,且由于是获取来自fgets函数,故可以自动换行 //fputs(recvbuf,stdout); // 检查退出命令 if (strcmp(recvbuf, "exit\n") == 0 || strcmp(recvbuf, "quit\n") == 0) { printf("Exiting...\n"); break; } // 服务端输入消息并发送给客户端 printf("Enter message to client: "); //fgets函数与fputs函数对应,是对get函数的封装。通俗理解就是读取字符 fgets(conn, sizeof(sendbuf), stdin); //write和read函数相对应,同理,参考上文。通俗理解为发送字符串 write(conn,sendbuf,ret); //接收或者发送完后,清空缓冲区,避免接受或发送残余 //例如:sendbuf第一回合为123456,sendbuf第二回合为7890 //则第二回合显示为789056,因为1234被覆盖掉了,但是56没有清空 memset(sendbuf,0,sizeof(sendbuf)); memset(recvbuf,0,sizeof(recvbuf)); } exit(0); }
client.c
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <arpa/inet.h> #define ERR_EXIT(m) do{ perror(m); exit(EXIT_FAILURE);} while(0) int main(int argc,char *argv[]) { int sockfd; if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) ERR_EXIT("socket"); struct sockaddr_in servaddr; memset(&servaddr,0,sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(10001); servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) < 0) ERR_EXIT("connect"); char sendbuf[1024] = {0}; char recvbuf[1024] = {0}; while(1) { // 客户端输入消息并发送给服务端 printf("Enter message to server: "); fgets(sendbuf,sizeof(sendbuf),stdin); //也有用send(sockfd, sendbuf, strlen(sendbuf), 0);来等替 write (sockfd,sendbuf,strlen(sendbuf)); // 检查退出命令 if (strcmp(sendbuf, "exit\n") == 0 || strcmp(sendbuf, "quit\n") == 0) { printf("Exiting...\n"); break; } // 接收服务器回复 read(sockfd,recvbuf,sizeof(recvbuf)); printf("Server: %s\n",recvbuf ); memset(sendbuf,0,sizeof(sendbuf)); memset(recvbuf,0,sizeof(recvbuf)); } //关闭socket close(sockfd); exit(0); }
同时在实验过程中,偶尔会遇到端口被使用,解决办法如下。
Address already in use 端口被占用
//查看当前占用端口命令 netstat -tanlp //或者你知道自己当前被占用的端口号,例如上文中适用的是10001 netstart -apn | grep 10001 //2918、3245、3623为pid值,可以再次查询pid来确定是谁启用的端口,避免误杀 ps -ef | grep 2918 //杀死进程(注意不是杀死端口,而是pid的端口) kill 2918 kill -9 2918(强制删除)