Try   HackMD

基本 AF_INET server (fork, signal, SIGPIPE, kill)

tags: network programming linux server fork SIGPIPE

Author: WhoAmI
email: kccddb@gmail.com

Date: 20220726
Copyright: CC BY-NC-SA

事實上真正實用的網路程式也不能照此例方式如此寫, 必需有搭配! 這是基本用法之一, 需要了解這基礎!

網路程式設計實用上最難的就是 "能動不代表能用" 但是可學的問題很多, 其中fork, select (epoll, epoll_wait 是加強版)是很重要的 system call, 一定要會. 這不一定只用於網路程式, 用於很多地方, 尤其未來 embedded system I/O 的部分! 另一重點是 thread!

udp 的部分比較容易, 請參考別的文件例如
Beej's Guide to Network Programming.
UDP Client/Server (select)

這裡額外提醒 MTU(Maximum Transmission Unit) 的問題, 例如 VPN, Jambo Frame, PPPoE, 的狀況

程式中 有些 地方 要特別注意 BUG0 BUG1

網路程式設計是 "盡信書不如無書" 的實際例子, 需要思考真正運用上的問題

不按牌理出牌亂搞, 只要有"電"還能達成任務 才算是可言"穩定"

通常 期末 team work: 傳送 4G 檔案, 可以續傳, 處理 網路斷線等問題+口試

道德经:千里之行始於足下

shell (bash): OnlineGDB 可以練習, 還是用自己的 Linux 方便

練習 find, grep, sudo, cp, tar, mv, rmdir, ip commands, bzip2, top, ps, pstree, strace, strip, vim, nano,
grep: Linux 匹配文字 grep 指令用法教學與範例, by G. T. Wang
了解Linux I/O 輸入與輸出重新導向,基礎概念教學, by G. T. Wang

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

BUG 除不盡 春風吹又生

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

一定要會的 Wireshark 可幫您

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

TCP Client/Server

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Linux Socket Layer

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

pstree

Simple TCP server

Please handle signals “SIGCHLD” and "SIGPIPE"

事實 真正實用的網路程式也不能照此如此寫 必需有搭配! 這是基本用法之一, 需要了解!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

以 TCP client/server 而言, send packet N 次 不代表 recv packet N 次!!!
timeout 也必須處理, select, epoll, setsockopt, SO_RCVTIMEO, SO_SNDTIMEO,
網路斷線 也需要處理
signal 要處理
byte order (尤其 不同 CPU)
16 bits, 32 bits, 64 bits,
網路不穩的問題
如果 server 有多組IP, 多組介面 可以 bind 不同的 IP

切記“網路程式能動不代表能用” 穩定是基本
您自己認為 可以, 有可能是 自己 測試 不完整

#include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define BUG0 1 #define SERVER_PORT 9999 int main(void) { int sockfd, new_fd; // listen on sock_fd, new connection on new_fd struct sockaddr_in server_addr; struct sockaddr_in client_addr; int sin_size; // “socklen_t sin_size;” pid_t pid; int yes=1; //STEP 1 sockfd = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM means TCP for AF_INET (IPv4) //STEP 2 setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)); /* SO_REUSEADDR Indicates that the rules used in validating addresses supplied in a bind(2) call should allow reuse of local addresses. For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address. NOTICE: If you have another process which binds on the same address (e.g., port, ip, tcp…), then you get “Address already in use” error. */ //It is better to clear server_addr. Don't use "bzero". memset(&(server_addr), '\0', sizeof(struct sockaddr_in)); server_addr.sin_family = AF_INET; //IPv4 // short network byte order (Host to Network Order) Network Order=htons(Host Order) server_addr.sin_port = htons(SERVER_PORT); server_addr.sin_addr.s_addr = INADDR_ANY; //For any address (IP, port) in this host //STEP 3 // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)); //If you don't set setsockopt, you get "Address already in use" error., when there is another process running on the same sockaddr. This is very important. //STEP 4 listen(sockfd, 10); //Be careful! //Perform TCP protocol while(1) { // main server loop sin_size = sizeof(struct sockaddr_in); //make sure the sin_size //STEP 5 new_fd = accept(sockfd, (struct sockaddr *)&client_addr,&sin_size); /* NAME accept - accept a connection on a socket #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); Notice that addr is a sockaddr pointer addlen is a socklen_t pointer RETURN a new file descriptor (fd) Hence client/server can communicate with this “new file descriptor”. It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. SEE ALSO int listen(int sockfd, int backlog); STEP 4 Common Mistakes at listen System Call listen: The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or… Hence we call passive socket for this socket of type SOCK_STREAM. */ //produce child process pid=fork(); if(pid==0){ //child process get pid==0 #if BUG0 close(sockfd); //why? #endif printf("Child process!\n"); printf("TCP connection from %s\n", inet_ntoa(client_addr.sin_addr)); //In fact, one must handle "n". For simplity, we ignore it now. //STEP 6 perform send/write recv/read n=send(new_fd, "Hello, Socket\n", 15, 0); //Check return value //Close socket ////STEP 7 close #if BUG0 close(new_fd); //why? #endif sleep(5); exit(0); }else if(pid>0){ printf("Parent process! Parent get PID of the child process=%d\n",pid); #if BUG0 close(new_fd); //why? #endif }else{ printf("Cannot fork()\n"); close(sockfd); exit(-1); } }//while #if BUG0 close(sockfd);//why? #endif exit(0); }

tcpc.c

#include <string.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_PORT 9999 #define SERVIP "127.0.0.1" #define BUFFERSIZE 1024 static int tcp_connect(int fd,char *ip,int port) { struct sockaddr_in addr; socklen_t addrlen; int sockfd; memset(&addr,0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip); sockfd=fd; if(fd<=0){ printf("open socket\n"); if((sockfd = socket(PF_INET, SOCK_STREAM, 0))<0){ perror("socket()"); exit(EXIT_FAILURE); } } addrlen=sizeof(struct sockaddr_in); if(connect(sockfd, (struct sockaddr *)&addr,addrlen)<0){ perror("connect()"); } return sockfd; } int main(void) { int sd=0; char buf[BUFFERSIZE+1]; int n; sd=tcp_connect(sd,SERVIP,SERVER_PORT); if(sd){ n=read(sd,buf,BUFFERSIZE); if(n>0)buf[n]=0; printf("recv:%s\n",buf); } close(sd); exit(0); }

$>: man 2 socket

#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

domain argument:

Name Purpose Man page
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
AF_INET6 IPv6 Internet protocols ipv6(7) /etc/gai.conf(5) getaddrinfo(3)

AF_NETLINK Kernel user interface device netlink(7)

AF_PACKET Low level packet interface packet(7)

//這是大概 基本觀念
//實用還有很多細節考量

TCP Client/Server (fork) 效能不好且佔用記憶體

struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}

network byte order

struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET /
in_port_t sin_port; / port in network byte order /
struct in_addr sin_addr; / internet address */
};

Internet address

​​​​   struct in_addr {
​​​​       uint32_t       s_addr;     /* address in network byte order */
​​​​   };

NOT THREAD SAFE!
int gethostname(char *name, size_t len);

The gethostbyname() and gethostbyaddr() functions return the hostent structure or a NULL pointer if an error occurs. On error, the h_errno variable holds an error number. When non-NULL, the return value may point at static data.
It’s not so good. If you need this, please use gethostbyname2().

MT-Unsafe: strtok(), gethostbyname(), gethostbyaddr(),

strace-trace system calls and signals
Check your program! For example, your program is “client”
$> strace ./client
Find the operation and open files.

IPv4 IPv6 priority (If you use “getaddrinfo”.)

See man 5 /etc/gai.conf

setsid - creates a session and sets the process group ID

setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the process group leader of the new process group, and has no controlling terminal. The process group ID and session ID of the calling process are set to the PID of the calling process. The calling process will be the only process in this new process group and in this new session.

// What is no controlling terminal? Your process will have no STDIN;
//e.g., [$> ./your_program &], your process has no controlling terminal.

small note:
The process group ID of any process equals the PID of the calling process. Thus, in particular, setsid() fails if the calling process is already a process group leader. USE pstree to see process relation.

char *cmd_buffer=“your_program_name”;

pid=fork();
if(pid==0){
setsid();
system(cmd_buffer);
exit(0);
}
See also “pstree”. “pstree” can show the processes relations.

Signal (man 7 signal )

SIGHUP 1 Term Hangup detected on controlling terminal or death of controlling process
SIGINT 2 Term Interrupt from keyboard
SIGQUIT 3 Core Quit from keyboard
SIGILL 4 Core Illegal Instruction
SIGABRT 6 Core Abort signal from abort(3)
SIGFPE 8 Core Floating-point exception
SIGKILL 9 Term Kill signal
SIGSEGV 11 Core Invalid memory reference
SIGPIPE 13 Term Broken pipe: write to pipe with no readers; see pipe(7)
SIGALRM 14 Term Timer signal from alarm(2)
SIGTERM 15 Term Termination signal
SIGCHLD 20,17,18 Ign Child stopped or terminated
more…

Term : Default action is to terminate the process.
Core : Default action is to terminate the process and dump core
Ign :Default action is to ignore the signal. (請看 SIGCHLD )

The signals SIGKILL cannot be caught, blocked, or ignored. (#>: kill -9 pid 就是)

SIGTERM can be caught! (一般 #>: kill pid 就是)
SIGSEGV 是程式執行錯誤的 Signal 例如

char *p do something… *p=100;

不合法的記憶體使用引起的 Signal (小心不一定會出現 SIGSEGV Signal, Why?)
這可能讓程式不穩定~有時可, 有時不行

SIGPIPE write to pipe with no readers, 例如 TCP send ~ 水管斷了

SIGCHLD Child stopped or terminated,
Ign 表示 Default action is to ignore the signal.(是 Parent 知道SIGCHLD , 不是該程式) 如下圖

運作流程:
Process (含 Signal Table, …)
Process 註冊 signal handler (hander_function address) 至 Signal Table
//e.g., signal(SIGPIPE , hander_function )

發生 signal 的意外 (除了 SIGKILL與特別嚴重 signal 以外 )
OS call hander_function(signal_number)

順便一提 killall kill processes by name

​​​​         killall program_name
​​​​                   kill process "program_name"
​​​​         killall ex1
​​​​                  = kill ex1-pid

kill system call
You can use “kill” system call in your program. Then you can kill (send signal) processes by PID;

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);

process1 kill(999,SIGTERM);>process2 (pid= 999)

send signal SIGTERM to pid 999 |
process2 will die.

有關byte order 可參考很好的文件 Beej’s Guide to Network Programming.

Homework:

  1. BUG0 是 0 會如何? 如何觀察現象?
  2. sleep(5); 改 sleep(100) 執行多個 clients 和現象?
  3. 練習 pstree, strace, top, strip, ldd, netstat 練習
  4. 用 telnet 測試
  5. Fast Common Gateway Interface (FastCGI) 改進了什麼? Why?
  6. 運用 bash 自動產生多個 clients 進行現象觀察