linux下socket程式設計基礎示例
阿新 • • 發佈:2018-12-09
本文主要用於記錄(因為有道雲容易丟失資料),程式碼並不規範,所有的內容都解除安裝main()函式裡面了,主要目的是為了方便自己理清流程。
服務端的程式碼:
#include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<sys/time.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/ioctl.h> #include<signal.h> #include<errno.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include "cs1.h" void log_error(){ printf("errno = %s\n", strerror(errno)); } void sig_handle(int sig){ printf("接收到了一個訊號: %d\n", sig); switch(sig){ case SIGPIPE: printf("收到SIGPIPE...\n"); break; } } int main(){ //定義變數 int server_fd; int client_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr;//客戶端 int client_addr_len; int bind_res; int listen_res; int select_res; fd_set fd_read_set; fd_set temp_read_set; fd_set fd_write_set; fd_set temp_write_set; int select_count = 1; int fd_arr[1000]; int temp_fd; int ioctl_res; int read_len; int write_len; char read_buf[1024]; char* client_ip; int client_port; int write_res; //設定訊號處理函式 //對一個對端已經關閉的socket呼叫兩次write, //第二次將會生成SIGPIPE訊號, 該訊號預設結束程序 struct sigaction sa; sa.sa_handler = sig_handle; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGPIPE, &sa, NULL); //初始化 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = inet_addr(IP); //建立socket server_fd = socket(AF_INET, SOCK_STREAM, 0); if(server_fd == -1){ printf("socket()呼叫失敗...\n"); log_error(); exit(EXIT_FAILURE); } printf("server_fd = %d\n", server_fd); fd_arr[0] = server_fd; //bind bind_res = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); if(bind_res == -1){ printf("bind()呼叫失敗...\n"); log_error(); exit(EXIT_FAILURE); } //listen listen_res = listen(server_fd, 5); if(listen_res == -1){ printf("listen()呼叫失敗...\n"); log_error(); exit(EXIT_FAILURE); } //初始化fd_set FD_ZERO(&fd_read_set); FD_ZERO(&fd_write_set); FD_SET(server_fd, &fd_read_set); while(1){ temp_read_set = fd_read_set; temp_write_set = fd_write_set; printf("select()呼叫之前----------------------------------\n"); //select()每次呼叫之後都會改變傳進去的集合 //將準備好的fd留在集合裡面,將沒有準備好的從集合裡面刪除 //這是很關鍵的一點 select_res = select(FD_SETSIZE, &temp_read_set,\ &temp_write_set, NULL, NULL); if(select_res == -1){ printf("select()發生了錯誤\n"); log_error(); exit(EXIT_FAILURE); } printf("準備好了的fd個數 = %d\n", select_res); printf("select_count = %d\n", select_count); for(int i = 0; i < select_count; i++){ temp_fd = fd_arr[i]; printf("temp_fd = %d.......................\n", temp_fd); //printf("server_fd = %d\n", server_fd); if(temp_fd == server_fd){ //服務端準備好了 if(FD_ISSET(temp_fd, &temp_read_set)){ printf("服務端讀準備好了...\n"); client_addr_len = sizeof(client_addr); client_fd = accept(server_fd,\ (struct sockaddr*)&client_addr,&client_addr_len); if(client_fd == -1){ printf("accept()呼叫失敗\n"); log_error(); continue; } if(select_count < 1000){ fd_arr[select_count] = client_fd; select_count++; FD_SET(client_fd, &fd_read_set); FD_SET(client_fd, &fd_write_set); //break; }else{ printf("fd_arr已經滿了,不再接收\n"); } } }else{ //客戶端準備好了 if(FD_ISSET(temp_fd, &temp_read_set)){ printf("客戶端讀準備好了...\n"); ioctl_res = ioctl(temp_fd, FIONREAD, &read_len); if(ioctl_res == -1){ printf("ioctl()呼叫發生錯誤\n"); log_error(); exit(EXIT_FAILURE); } printf("read_len = %d\n", read_len); read_len = read_len > 1022 ? 1023 : read_len; if(read_len > 0){ read(temp_fd, read_buf, read_len); read_buf[read_len] = '\0'; printf("temp_fd = %d, read_buf = %s\n", temp_fd,\ read_buf); } } //寫準備好了 if(FD_ISSET(temp_fd, &temp_write_set)){ printf("客戶端寫準備好了\n"); //client_ip = inet_ntoa(client_addr.sin_addr); //client_port = ntohs(client_addr.sin_port); client_ip = read_buf; if(strlen(client_ip) == 0){ printf("要傳送的長度為0\n"); continue; } write_res = write(temp_fd, client_ip, strlen(client_ip)); printf("write_res = %d\n", write_res); if(write_res == -1){ printf("write()發生錯誤..\n"); log_error(); //可能是客戶端關閉了造成的 FD_CLR(temp_fd, &fd_write_set); FD_CLR(temp_fd, &fd_read_set); } //write(temp_fd, &client_port, sizeof(client_port)); } } sleep(3); } } return 0; }
客戶端的程式碼:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> #include<sys/socket.h> #include<netinet/in.h> #include<arpa/inet.h> #include<sys/ioctl.h> #include<errno.h> #include<string.h> #include "cs1.h" int main(){ int sfd; struct sockaddr_in server_addr; char read_buf[1024]; int addr_len; int co_res; int ioctl_res; int read_len; //建立socket sfd = socket(AF_INET, SOCK_STREAM, 0); if(sfd == -1){ printf("建立socket失敗\n"); strerror(errno); exit(EXIT_FAILURE); } printf("sfd = %d\n", sfd); //連線socket server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT);//對port使用short server_addr.sin_addr.s_addr = inet_addr(IP); addr_len = sizeof(server_addr); printf("addr_len = %d\n", addr_len); co_res = connect(sfd, (struct sockaddr*)(&server_addr), addr_len); if(co_res != 0){ printf("連線失敗\n"); close(sfd); exit(EXIT_FAILURE); } printf("connect()呼叫返回,且返回值不等於0...\n"); while(1){ printf("迴圈開始----------------------------------\n"); //向socket裡面寫入資料 write(sfd, "a", 1); ioctl_res = ioctl(sfd, FIONREAD, &read_len); if(ioctl_res == -1){ printf("ioctl()呼叫失敗...\n"); exit(EXIT_FAILURE); } if(read_len == 0){ printf("read_len等於0...\n"); //exit(EXIT_FAILURE); } printf("read_len = %d \n", read_len); read_len = read_len > 1022 ? 1023 : read_len; if(read_len > 0){ read(sfd, read_buf, read_len); read_buf[read_len] = '\0'; printf("客戶端讀取到的資料 = %s\n", read_buf); } printf("迴圈結束----------------------------------\n"); sleep(3); } return 0; }