1. Basic communication process between client and server in network programming
2. Server and client programming < iterative server >
2.1. Iterative server programming
2.1.1. Command line parameter parsing
- The server parameter only has the port number. Add a help parameter < - H > to explain the usage of this command
- The code is written as follows
void print_usage(char *progname)
{
printf("%s usage: \n", progname);
printf("-p(--port): sepcify server port.\n");
printf("-h(--Help): print this help information.\n");
return ;
}
{
int port = 0;
int ch;
struct option opts[] = {
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )
{
switch(ch)
{
case 'p':
port=atoi(optarg);//String - > integer
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if(!port) //Command not used normally
{
print_usage(argv[0]);
return 0;
}
}
2.1.2. Create server socket
{
socket_fd = socket(AF_INET, SOCK_STREAM, 0);//IPV4 TCP
if(socket_fd < 0)
{
printf("Create socket failure : %s\n", strerror(errno));
return -1;
}
printf("Create socket [%d] successful!\n", socket_fd);
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));//Prevent program port reuse error
}
2.1.3. bind bind port and ip and enable listen
{
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port); // Host byte order - > network byte order
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // Monitor all IP hosts ----- > Network
rv = bind(socket_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rv < 0)
{
printf("Socket[%d] bind on port [%d] failure : %s\n",socket_fd,port,strerror(errno));
return -2;
}
printf("Socket[%d] bind on port [%d] successful!\n", socket_fd, port);
//listen
listen(socket_fd, 13);
}
2.1.4. Open accept
- Accept is a blocking function. When no client connects to the server, the program will block and not return until a client connects. When the client calls the connect function, the accept function of the server will be triggered to return, and the TCP connection will be established.
- code implementation
//accept
clifd = accept(socket_fd, (struct sockaddr *)&cliaddr, &len);
if(clifd < 0)
{
printf("Accept new client failure:%s\n", strerror(errno));
continue;
}
printf("Accept new client [%s:%d] successfully!\n"
,inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));//IP and port of the client
2.1.5. Read and write to the client through file IO system call
- Read client programming, using read system call
{
memset(buf, 0, sizeof(buf));
rv = read(clifd, buf, sizeof(buf));
if(rv < 0)
{
printf("Read from client by clifd[%d] failure: %s\n", clifd , strerror(errno));
close(clifd);
continue;
}
else if(rv == 0)
{
printf("Clifd[%d] get disconnected\n", clifd);
close(clifd);
continue;
}
else if(rv > 0)
{
printf("Read [%d] byte data from client clifd[%d] : %s\n", rv, clifd, buf);
}
}
- write messages to the client. Note that you do not want to continue communication after writing. You need to close the client descriptor.
{
rv = write(clifd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("write to client by clifd[%d] failure: %s\n", clifd , strerror(errno));
close(clifd);
continue;
}
printf("Close client fd[%d]\n", clifd);
close(clifd);
}
- Before shutting down the server, you also need to close the socket descriptor created before listening.
2.2. Client programming
2.2.1 client command line parameter resolution (with domain name resolution function)
- The client parameters include server IP address and server port. Add the help command < - H >, and add the domain name command < - d >. This code only resolves the domain name and prints its IP address. Since there is no public IP, it cannot be tested. baidu.com can resolve its IP address
{
int ch;
struct option opts[] = {
{"ipaddr", required_argument, NULL, 'i'},
{"port", required_argument, NULL, 'p'},
{"domain_name", required_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
};
while( (ch=getopt_long(argc, argv, "i:p:d:h", opts, NULL)) != -1 )
{
switch(ch)
{
case 'i':
servip=optarg;
break;
case 'p':
port=atoi(optarg);
break;
case 'd':
ser_domain_name=optarg;
break;
case 'h':
print_usage(argv[0]);
return 0;
}
}
if( !servip || !port || !ser_domain_name)
{
print_usage(argv[0]);
return 0;
}
//Print domain name and resolved IP address
p = gethostbyname(ser_domain_name);
printf("domain_name : %s servip : %s\n", ser_domain_name, inet_ntoa(*((struct in_addr *)p->h_addr)));
}
2.2.2. Create client socket
- Use IPV4 and TCP communication
{
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd < 0)
{
printf("Create socket failure :%s\n", strerror(errno));
return -1;
}
printf("Create socket [%d] successful!\n", socket_fd);
}
2.2.3. connect to the server
- Pass in the parameters through the specification, and pay attention to byte order and string conversion
{
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);// Main ---- > Network
inet_aton(servip, &servaddr.sin_addr); //String ----- > network byte order
rv = connect(socket_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rv < 0)
{
printf("Connect to server [%s:%d] failure : %s\n", servip, port, strerror(errno));
return -2;
}
printf("Connect to server [%s:%d] successful!\n", servip, port);
}
2.2.4. Read and write to the server through file IO system call
- Like the server, read and write are used to read and write messages
- Note that after the communication between the client and the server ends, the socket descriptor can be closed if you do not want to continue the communication
{
rv = write(socket_fd, MSG_STR, strlen(MSG_STR));
if(rv < 0)
{
printf("Write to server by socket_fd [%d] failure : %s\n", socket_fd, strerror(errno));
break;
}
//read
memset(buf, 0, sizeof(buf));
rv = read(socket_fd, buf, sizeof(buf));
if(rv < 0)
{
printf("Read from server by sockfd[%d] failure: %s\n", socket_fd , strerror(errno));
break;
}
else if(0 == rv)
{
printf("Socketfd[%d] get disconnected\n", socket_fd);
break;
}
else if(rv > 0)
{
printf("Read [%d] byte from server socket_fd [%d] : %s\n", rv, socket_fd, buf);
}
close(socket_fd);
}
3. Server + multi process programming
3.1. Brief introduction to multi process programming
- The iterative server can only communicate with one client at a time. In fact, a large number of clients will access and communicate with the server. At this time, the iterative server is no longer applicable.
- The concurrent server is realized by multi process programming, and the structural block diagram is as follows
3.2 programming
- Multi process programming, modify the original server code, and open the sub process to communicate with the client after receiving the client connection request
{
pid = fork();
if(pid < 0)
{
printf("fork() create child process failure : %s\n", strerror(errno));
close(cli_fd);
}
else if(pid > 0) // Function of parent process
{
close(cli_fd); //The parent process does not require a client descriptor
continue;
}
else if(0 == pid)
{
close(socket_fd); //The child process does not need the fd. The fd is used for listening. After listening, a new fd is used
....//Communication between server and client
}
}