This is the client version of the sequel to Introduction to Socket API in C Language, Part 1 Server Edition.
Last time, I created server software, so this time I will look at the mechanism of communication between different hosts while creating client software that uses the socket API.
Since the previous server software used the TCP protocol, this client software also uses the TCP protocol.
Although the client program and the server program have similar parts such as the structure of the data to be exchanged and the method of sending and receiving, there are some differences, so let's pay attention to those parts.
This time it is client software, so I created it on Mac OS X and ran it.
Mac OS X 10.10.5. The compiler is gcc. Introduced in Xcode.
The header file path was stored in a deep hierarchy of /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
in my environment.
tcpc.c
#include <stdio.h> //printf(), fprintf(), perror()
#include <sys/socket.h> //socket(), connect(), recv()
#include <arpa/inet.h> // struct sockaddr_in, struct sockaddr, inet_ntoa(), inet_aton()
#include <stdlib.h> //atoi(), exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <string.h> //memset()
#include <unistd.h> //close()
#define MSGSIZE 32
#define MAX_MSGSIZE 1024
#define BUFSIZE (MSGSIZE + 1)
int main(int argc, char* argv[]) {
int sock; //local socket descripter
struct sockaddr_in servSockAddr; //server internet socket address
unsigned short servPort; //server port number
char recvBuffer[BUFSIZE]; //receive temporary buffer
int byteRcvd, totalBytesRcvd; //received buffer size
if (argc != 3) {
fprintf(stderr, "argument count mismatch error.\n");
exit(EXIT_FAILURE);
}
memset(&servSockAddr, 0, sizeof(servSockAddr));
servSockAddr.sin_family = AF_INET;
if (inet_aton(argv[1], &servSockAddr.sin_addr) == 0) {
fprintf(stderr, "Invalid IP Address.\n");
exit(EXIT_FAILURE);
}
if ((servPort = (unsigned short) atoi(argv[2])) == 0) {
fprintf(stderr, "invalid port number.\n");
exit(EXIT_FAILURE);
}
servSockAddr.sin_port = htons(servPort);
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0 ){
perror("socket() failed.");
exit(EXIT_FAILURE);
}
if (connect(sock, (struct sockaddr*) &servSockAddr, sizeof(servSockAddr)) < 0) {
perror("connect() failed.");
exit(EXIT_FAILURE);
}
printf("connect to %s\n", inet_ntoa(servSockAddr.sin_addr));
totalBytesRcvd = 0;
while (totalBytesRcvd < MAX_MSGSIZE) {
if ((byteRcvd = recv(sock, recvBuffer, MSGSIZE, 0)) > 0) {
recvBuffer[byteRcvd] = '\0';
printf("%s", recvBuffer);
totalBytesRcvd += byteRcvd;
} else if(byteRcvd == 0){
perror("ERR_EMPTY_RESPONSE");
exit(EXIT_FAILURE);
} else {
perror("recv() failed.");
exit(EXIT_FAILURE);
}
}
printf("\n");
close(sock);
return EXIT_SUCCESS;
}
Loading the required headers. All I had to do was load the same thing as on the server. The comment on the right describes what is being read for use. There are some that can be further included and used from there, but I will omit them.
It is not necessary this time, but it will be necessary from the next time, so define the symbol constants. I think that the number of symbol constants will increase in the future.
Allocate a memory area for the required data type. The definition of data type in network programming was described in Part 1, so please refer to that as well.
Argument check.
At the time of execution, the IP address of the connection destination host is a character string in dot decimal notation (IPv4), and the port number can be specified by any number.
In the case of the server version, it was specified to associate the port number with the local host, but in the case of the client version, specify the IP address and port number of the remote host to connect to.
As in the case of the server edition, clear the area of the sockaddr_in structure to zero. The difference from the server edition is that the address information of the connection destination host is stored in this sockaddr_in structure. This time we will not prepare a sockaddr_in structure for the local host.
As in the case of the server edition, specify AF_INET, which indicates that it is an Internet address family (IPv4), in sin_family of the sockaddr_in structure.
Use the inet_aton function to convert a dot decimal notation (IPv4) string to a binary representation in network byte order and store it in the sin_addr field. Unlike the time of the server edition, it is stored by passing a pointer, but there are several methods just because it took such a method.
In the sample for beginners, the inet_addr function that converts the dot decimal notation (IPv4) character string to the binary representation value and returns it as the return value is used, but in the inet_addr function, the return value at the time of error is -1. I used the inet_aton function this time because it points to a valid IP address (255.255.255.255) and there is something about the error value.
As with the server version, the number of the port number of the argument is converted to the binary representation of the network byte order and stored in sin_port of the sockaddr_in structure.
Use the system call socket () to request the creation of a socket. Each argument is the same as in the server edition, so please check that.
This is the biggest difference from the server program. The client calls the connect () system call to establish a connection with the server program.
Specify the socket descriptor that identifies the socket created earlier in the first argument, the sockaddr_in structure that contains the IP address and port number of the server in the second argument, and its size in the third argument.
It is the same as the program for the server, but since the socket API is a general-purpose API, the pointer of the sockaddr_in structure is cast to the pointer of the sockaddr structure, which is a general-purpose data type.
You may have one question here.
Isn't it necessary to connect the socket of the local host with the IP address and port number using the bind () system call as in the server edition? When.
That's right.
Even the client who is in the position to start the connection needs to have the address information connected to the socket in order to communicate.
In fact, when the client calls the connect () system call, the socket structure is automatically populated with the local IP address of the local host and the value of the open port number, along with the address information to connect to.
You can explicitly bind the local address information to a socket by calling the bind () system call before connect (), just as you would on a server. However, it can be said that this specification is not usually necessary.
This is because the client needs to know the server's address information in advance to start a connection with the server, whereas the server does not need to know the client's address information in advance.
When connect () is completed normally and control is restored, the 3-way handshake is completed successfully, so you can check the status using netstat
, which was also used for the server edition.
Run the program created in the server edition (just change it so that close () is not called), and in the client environment ./a.out xxx.xxx.xxx.xxx 8080
(xxx.xxx.xxx.xxx is the server If you execute netstat -t
on another terminal, you will see a message like tcp4 0 0 192.168.xxx.xxx.65486 xxx.xxx.xxx.xxx.8080 ESTABLISHED
. I can do it.
The port number 65486 is the port number of the client host automatically assigned from the port opened by the OS at the time of connect ().
If the connection is successfully established using connect (), it means that the connection with the server has been established, so a message indicating that is displayed.
I wrote the code for the next time a little earlier, but what I should pay attention to now is (byteRcvd = recv (sock, recvBuffer, MSGSIZE, 0)
on line 54 and byteRcvd == 0 on line 58. This is the
place.
recv () is a system call that fetches the bytes stored in the receive buffer queue into the user process. The receive buffer can be confirmed as Recv-Q in netstat
.
recvBuffer means the start address of the memory area where the received byte string is stored, and MSGSIZE specifies the size to get. Normally, the received byte string should not contain a null character, so when using a string output function etc., add the null character to the end of the get byte string.
0 is specified in the 4th argument, but this is a flag to change the behavior of recv. 0 means the default behavior of blocking the operation of the program until it is receivable.
The return value is the number of bytes received, but if it is 0, it means that the program you are communicating with has disconnected the TCP connection.
I'd like you to check the program of the previous server edition, but that program disconnects immediately after accepting and making it possible to send and receive data between the client and the server.
Therefore, if you execute this program and the previous program on the client and server respectively, the client side will display the message ʻERR_EMPTY_RESPONSE` and exit.
This message is named after the error message I received when I was disconnected from the server the last time I used Chrome as client software.
This time, I created TCP client software and was able to communicate with the previous server program.
I'm sorry to go back and forth, but next time I would like to extend the server software, send meaningful messages to the client, and expand the client software that receives it depending on the total volume. ..
-Network construction by TCP / IP <Vol.1> Principle / Protocol / Architecture -[TCP / IP network experiment programming understood from the basics-Linux / FreeBSD compatible](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3 % 82% 89% E3% 82% 8F% E3% 81% 8B% E3% 82% 8BTCP-IP-% E3% 83% 8D% E3% 83% 83% E3% 83% 88% E3% 83% AF% E3% 83% BC% E3% 82% AF% E5% AE% 9F% E9% A8% 93% E3% 83% 97% E3% 83% AD% E3% 82% B0% E3% 83% A9% E3% 83% 9F% E3% 83% B3% E3% 82% B0% E2% 80% 95Linux-FreeBSD% E5% AF% BE% E5% BF% 9C-% E6% 9D% 91% E5% B1% B1 / dp / 4274065847 / ref = sr_1_4? s = books & ie = UTF8 & qid = 1471270171 & sr = 1-4 & keywords =% E5% 9F% BA% E7% A4% 8E% E3% 81% 8B% E3% 82% 89% E3% 82% 8F% E3% 81% 8B% E3% 82% 8Btcp% 2Fip) -[TCP / IP Socket Programming C Language](https://www.amazon.co.jp/TCP-IP%E3%82%BD%E3%82%B1%E3%83%83%E3%83% 88% E3% 83% 97% E3% 83% AD% E3% 82% B0% E3% 83% A9% E3% 83% 9F% E3% 83% B3% E3% 82% B0-C% E8% A8% 80% E8% AA% 9E% E7% B7% A8-Michael-Donahoo / dp / 4274065197) -[Detailed Linux Kernel 3rd Edition](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Linux%E3%82%AB%E3%83%BC%E3 % 83% 8D% E3% 83% AB-% E7% AC% AC3% E7% 89% 88-Daniel-Bovet / dp / 4873111313X)
-Network construction by TCP / IP <Vol.1> Principle / Protocol / Architecture -[TCP / IP network experiment programming understood from the basics-Linux / FreeBSD compatible](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3 % 82% 89% E3% 82% 8F% E3% 81% 8B% E3% 82% 8BTCP-IP-% E3% 83% 8D% E3% 83% 83% E3% 83% 88% E3% 83% AF% E3% 83% BC% E3% 82% AF% E5% AE% 9F% E9% A8% 93% E3% 83% 97% E3% 83% AD% E3% 82% B0% E3% 83% A9% E3% 83% 9F% E3% 83% B3% E3% 82% B0% E2% 80% 95Linux-FreeBSD% E5% AF% BE% E5% BF% 9C-% E6% 9D% 91% E5% B1% B1 / dp / 4274065847 / ref = sr_1_4? s = books & ie = UTF8 & qid = 1471270171 & sr = 1-4 & keywords =% E5% 9F% BA% E7% A4% 8E% E3% 81% 8B% E3% 82% 89% E3% 82% 8F% E3% 81% 8B% E3% 82% 8Btcp% 2Fip) -[TCP / IP Socket Programming C Language](https://www.amazon.co.jp/TCP-IP%E3%82%BD%E3%82%B1%E3%83%83%E3%83% 88% E3% 83% 97% E3% 83% AD% E3% 82% B0% E3% 83% A9% E3% 83% 9F% E3% 83% B3% E3% 82% B0-C% E8% A8% 80% E8% AA% 9E% E7% B7% A8-Michael-Donahoo / dp / 4274065197) -[Detailed Linux Kernel 3rd Edition](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Linux%E3%82%AB%E3%83%BC%E3 % 83% 8D% E3% 83% AB-% E7% AC% AC3% E7% 89% 88-Daniel-Bovet / dp / 4873111313X)
Recommended Posts