1. 程式人生 > >linux下socket程式設計基礎示例

linux下socket程式設計基礎示例

本文主要用於記錄(因為有道雲容易丟失資料),程式碼並不規範,所有的內容都解除安裝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;
}