In daily development, we often encounter situations where Linux hosts act as TCP servers. As long as the Linux system has an Ethernet port or WIFI, it can act as a TCP/UDP server or client. TCP communication in Liunx environment is simpler than PC, because the Linux system API has built-in TCP/UDP Socket communication functions. If Windows wants to perform TCP/UDP communication, it is also necessary to install the corresponding software library to start and load the relevant development environment (such as QT, MFC, etc.). The target group of this article is novices who have a very simple foundation in Linux and C language. The language style is as easy to understand as possible, and strives to make it understandable to novices.
I won't go into details about the hierarchical structure of TCP communication. TCP belongs to the transport layer in the OSI reference model. The application layer protocols of the upper layer include HTTP, FTP, etc. These protocols must be based on TCP Socket communication. The network layer protocol below TCP is the IP protocol. TCP communication must be established on the IP layer, and the IP layer is established on the underlying hardware of the data link layer and the physical layer.
Next, the focus is on the Linux TCP communication process. The TCP server must perform the socket(), bind(), listen(), and accept() operations at the beginning. The socket() operation is to establish the TCP send and receive socket, which is the data basis for communication; the bind() operation is to bind the socket to the TCP server. This operation is mainly related to the port opened by the TCP server. When the port is occupied, the bind() operation fails; listen(), as the name suggests, is to answer the connection request of the TCP client; accept() is to accept the request of the TCP client. accept() is a blocking operation. If we understand it with our conventional thinking, accept() and listen() should be the same thing and blocked, because we rarely encounter TCP servers rejecting TCP client connection requests, but the program requires strictness, and both listen() and accept() operations must be present and the order cannot be changed at will.
After the operation is established, it is the recv() receiving ((some articles will write recv() as read(), the meaning is exactly the same)) and send() sending operations (some articles will write send() as write(), the meaning is exactly the same), among which recv() is a blocking operation, so Linux must use multi-threaded applications to realize the integration of receiving and sending of TCP servers. In Linux, multi-threading is true multi-threading (except for single-core single-threaded CPUs), not time slice round-robin scheduling. A newly opened thread runs completely independently of the original thread. Communication between threads must use semaphores, message queues or mailboxes. In this TCP communication, since the TCP server needs to perform sending and receiving operations at the same time, and the sending and receiving operations are completely independent, the most important thing is that the receiving operation is still blocked, then multi-threading must be enabled. For details, see the code below.
The first step is to complete the preparation of the TCP server. The first step is to set the working mode of the TCP server (the default is AF_INET, no need to go into details, official setting), IP address and port:
struct sockaddr_in bindaddr;
bindaddr.sin_family=AF_INET;
bindaddr.sin_addr.s_addr=inet_addr(argv[1]);
bindaddr.sin_port=htons(atoi(argv[2]));
Here I use argv to input a string to set the IP address and port. This argv is the content input when the program is executed, separated by spaces. For example, when executing the main program in Linux:
#./main aaa bbb
argv[0] is "./main", argv[1] is "aaa", and argv[2] is "bbb".
Then the four operations of socket(), bind(), listen(), and accept() are executed in sequence:
int ret,fd_socket;
fd_socket=socket(AF_INET,SOCK_STREAM,0);
if(fd_socket==-1)
{
printf("Socket initialization failed!\n");
return -1;
}
ret=bind(fd_socket,(struct sockaddr *)&bindaddr,addrsize);
if(ret==-1)
{
printf("Socket binding failed!\n");
return -1;
}
ret=listen(fd_socket,5);
if(ret==-1)
{
printf("Server monitoring failed!\n");
return -1;
}
newsock=accept(fd_socket,(struct sockaddr *)&boyaddr,&addrsize);
In actual applications, the most likely cause of premature program exit is the bind() operation, that is, the port is occupied. The accept() operation here is blocked. If there is no TCP client connection, the program will be blocked at this point.
Then create another thread:
pthread_t id1;
pthread_create(&id1,NULL,Scanf_Thread,NULL);
Add thread content elsewhere in the main() file. My design here is to first input an integer to represent the length of the data to be sent, and then dynamically allocate a memory space for the send buffer. If it is not enough, fill it with \0, and if it is too much, discard it:
int newsock;
void *Scanf_Thread(void *arg)
{
int len;
char *s;
while(1)
{
bzero(s,0);
scanf("%d",&len);
s=(char *)malloc(len);
scanf("%s",s);
send(newsock,s,len,0);
free(s);
}
}
Add the following to while(1) of the main function:
while(1)
{
bzero(recvbuf,100);
recv(newsock,recvbuf,100,0);
printf("Data sent by client: %s\n",recvbuf);
}
recvbuf is a char string. In this way, a simple TCP server transceiver program is ready. Let's see the effect first. Run the program and enter IP 169.254.122.7 and port 6666:
Set the sender and receiver assistant to TCP client mode, enter IP 169.254.122.7, port 6666, and click Connect:
The client sends multiple pieces of data to the server:
The server sends multiple pieces of data to the client: