Basic concepts of stream sockets


The so-called socket (Socket) is an abstraction of endpoints for bidirectional communication between application processes on different hosts in the network. A socket is one end of process communication on the network, providing a mechanism for application layer processes to exchange data using network protocols

Basic concepts of stream sockets

Insert picture description here

Create socket

int socket(int domain,int type,int protocol);
返回值:文件描述符表示成功,-1表示错误,errno记录错误代码
  • The domain name domain
    of the socket represents the address family of the socket

    domain name Address family
    AF_UNIX, AF_LOCAL For local communication
    AP_INET, PF_INET IPv4, Internet Protocol
    AF_INET6 IPv6, Internet protocol
    AF_IPX Novell Network Protocol
  • Type of socket

    The two most commonly used values ​​are SOCK_STREAMandSOCK_DGRAM

    1. SOCK_STREAM streaming socket

      The streaming socket does not retain any message boundaries, it simply passes all its data to the receiver.

      In addition, the data is read by the receiving end strictly in the order in which it was written. In the IP protocol, packets can be forwarded to the destination according to different routes, so packet out-of-sequence will occur at the data receiving end, and the streaming socket can ensure that the receiving end correctly receives the data in the order in which the data was sent. If an error occurs during transmission, the streaming mechanism automatically performs error correction.

      Streaming sockets provide reliable data connections, using a connection-oriented approach.

    2. SOCK_DGRAM datagram socket

      Used for connectionless communication, that is, the two parties do not need to establish any connection before communication, as long as a non-connection datagram socket is created.

      UDP protocol is a typical datagram communication method.

      The datagram socket does not know whether data loss occurred during transmission , so the discovery and correction of data errors can only be guaranteed by the application layer.

  • Protocol used

    Generally, it is 0, which means that the system automatically selects the appropriate protocol type under the domain currently set.

Bind local address

After the socket is created, the socket is in a state not associated with any protocol address.

If the sockets on two different hosts need to be connected without an address, they cannot communicate. So use bind() to bind the socket to the specified protocol family.

int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
返回值:0表示成功,-1表示失败,errno记录错误代码
  • sockfd

    Use the result returned by the socket() function

  • myaddr

    The address pointer assigned to the socket.

    struct sockaddr{
          
          //通用套接字地址
    	sa_family_t sa_family;//地址族
    	char        sa_data[4];//地址数据
    }
    不会直接使用该结构进行地址设置,他只是作为一个通用类型,以确定所有其他具体地址的结构、
    
    IPv4套接字地址
    struct sockaddr_in{
          
          
        sa_family_t sin_family;//地址族
        uint16_t sin_port;//端口
        struct in_addr sin_addr;//ip地址
        unsigned char sin_zero[8];//占位字节
    }
    struct in_addr{
          
          
        uint32_t s_addr;//IP地址
    }
    
  • addrlen

    the length of myaddr address

Note that the server needs to be bound, but the client does not. Because the connection request is usually initiated by the client, the client socket is automatically bound to a temporary port and address by the kernel after the connection request is sent. As a server, it generally works in a passive connection mode, so the listening socket must be bound to a well-known port by calling bind() explicitly to wait for the client to connect.

Connection request

int connect(int sockfd,const struct sockaddr *servaddr,socklen_t addrlen);
返回值:0表示成功。-1表示失败,errno记录错误代码

Used to establish a connection between the TCP client and the TCP server.

  • sockfd

    Socket generated by calling socket()

  • servaddr

    The server address that the client is ready to connect to

  • addrlen

    Server address length

Monitor function

int listen(int sockfd,int backlog);
返回值:0成功,-1失败。errno记录错误代码

Used for TCP server start monitoring

  • sockfd

    Socket for listening

  • backlog

    The length of the connection queue. It refers to the length of the queue for successfully establishing a TCP connection after completing the TCP three-way handshake. When the server executes the accept() operation, it removes a connection from the queue for subsequent processing. The default is 128

Receive request

int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
返回值:文件描述符表示成功,-1表示失败merrno记录错误代码
  • sockfd

    Socket for listening

  • cliaddr

    A pointer to the address structure used to receive the client socket address.

  • addrlen

    Pointer to the maximum length of the receiving socket address buffer.

If the function call is successful, its return value is a new socket descriptor, called a connection socket. The server uses this socket to communicate with the client that has established a connection. The original listening socket continues to receive subsequent connection requests from new clients. The connection socket is closed after the communication is completed, but the listening socket will continue to monitor until the entire application ends.

Insert picture description here

Socket IO operation

ssize_t read(int sockfd,void *buf, size_t count);
返回:非0 是所读字节数。0文件尾,-1失败, errno记录错误代码
  • sockfd

    Socket for read operations

  • buf

    Cache for storing read data

  • count

    Represents the maximum readable data byte length that can be received in this read, usually the size of the buffer pointed to by buf.

ssize_t write(int sockfd,const void *buf,size_t count);
返回值:非0表示所写字节数,0表示未写任何数据,-1表示失败,errno记录错误代码、
  • sockfd: socket for write operations
  • buf: Cache for storing data to be written
  • count: The length of data bytes to be written, usually the size of the output buffer pointed to by buf.

Close socket

int close(int sockfd);
int shutdown(int sockfd,int how);
0表示成功.-1失败
前者完全关闭,后者用于希望在完全关闭本地套接字前仍然可以从远端套接字继续接收数据,但不允许本地发送。通常用于即将结束通信的善后处理。

Parameter how

value Macro Description
0 SHUT_RD Do not allow local sockets to perform read operations
1 SHUT_WR Disallow local sockets for writing
2 SHUT_RDWR Equal to close

Programming realization

wins下,cygwin

服务器端
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE 512
int main(int argc,char *argv[]){
    
    
	int sockfd;//服务器套接字 
	int new_sockfd;//连接套集字 
	struct sockaddr_in server_addr;//服务器地址 sockaddr_in ipV4 
	struct sockaddr_in client_addr;//客户端地址
	socklen_t size;
	int portnumber;//端口号 
	
	char reqBuf[BUFSIZE];//接收缓存区
	int z;//read()返回值
	
	if(argc!=2){
    
    
		fprintf(stderr, " %s portnumber\n",argv[0] );
		exit(1);
	}
	if((portnumber=atoi(argv[1]))<0){
    
    
		fprintf(stderr, "%s portnumber\n",argv[0]);
		exit(1);
	}

	if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1){
    
    
		fprintf(stderr, "socket errot:%s\n",strerror(errno));
		exit(1);
	}

	memset(&server_addr,0,sizeof server_addr);
	server_addr.sin_family=AF_INET;//IP V4 协议族
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//host ip->net ,long type
	server_addr.sin_port=htons(portnumber);// 端口

	if((bind(sockfd,(struct sockaddr *)(&server_addr),sizeof server_addr))==-1){
    
    //绑定套接字到指定地址和端口
		fprintf(stderr, " bind error: %s\n",strerror(errno));
		exit(1);
	}
	if(listen(sockfd,128)==-1){
    
    //监听请求,将请求放入BackLog中,队列
		fprintf(stderr, " listen error:%s\n",strerror(errno));
		exit(1);
	}
	printf("wait for the request from client\n");
	while(1){
    
    

		size=sizeof(struct sockaddr_in);
		if((new_sockfd=accept(sockfd,(struct sockaddr *)(&client_addr),&size))==-1){
    
    
			fprintf(stderr, "accept error:%s\n", strerror(errno));
		}
		fprintf(stdout, "server got connection from %s\n", inet_ntoa(client_addr.sin_addr));
		while(1){
    
    //接收数据
			 z=read(new_sockfd,reqBuf,sizeof reqBuf); 
			if(z<0){
    
    
				fprintf(stderr, "read error:%s\n",strerror(errno));
				exit(1);
			}
			if(z==0){
    
    
				printf("colse new_sockfd\n");
				close(new_sockfd);
				break;
			}
			reqBuf[z]=0;
			printf("receive form client:   %s\n",reqBuf);
			printf("write down your message to client\n");
			fgets(reqBuf,sizeof reqBuf,stdin);
			z=write(new_sockfd,reqBuf,sizeof reqBuf);
			if(z<0){
    
    
				printf("write error");
				exit(1);
			}
		}
	}
}

客户端

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE 512
int main(int argc,char *argv[]){
    
    
	int sockfd;
	char buf[BUFSIZE];
	struct sockaddr_in server_addr;
	struct hostent *host;
	int portnumber;
	int nbytes;
	int z;
	char reqBuf[BUFSIZE];
	host=gethostbyname(argv[1]);
	portnumber=atoi(argv[2]);
	if((sockfd=socket(PF_INET,SOCK_STREAM,0))==-1){
    
    
		fprintf(stderr, "socket error:%s\n", strerror(errno));
		exit(1);
	}
	memset(&server_addr,0,sizeof server_addr);
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(portnumber);
	server_addr.sin_addr=*((struct in_addr*)host->h_addr);
	if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof server_addr)==-1){
    
    
		fprintf(stderr, "connect error:%s\n", strerror(errno));
		exit(1);
	} 
	printf("connect success!\n");
	
	
	while(1){
    
    
		printf("please input your message to server\n");
		if(!fgets(reqBuf,sizeof reqBuf,stdin)){
    
    
			printf("fgets error!\n");
			break;
		}
		z=strlen(reqBuf);
		if(z>0&&reqBuf[--z]=='\n')//末尾添加NULL,同时去掉\n 
			reqBuf[z]=0;
		if(z==0) continue;
		//input QUIT to exit
		if(!strcasecmp(reqBuf,"QUIT")){
    
    
			printf("the client colsed.please enter any key to exit\n");
			getchar();
			write(sockfd,reqBuf,strlen(reqBuf));
			break;
		} 
		z=write(sockfd,reqBuf,strlen(reqBuf));
		
		if(z<0){
    
    
			fprintf(stderr, "write error:%s\n",strerror(errno));
			exit(1);
		}
		if((z=read(sockfd,reqBuf,sizeof(reqBuf)))==-1){
    
    
			fprintf(stderr, "read error:%s\n",strerror(errno));			
			exit(1);
		}
		if(z==0){
    
    
			printf("the server colsed.please enter any key to exit\n");
			getchar(); 
			break;
		} 
		reqBuf[z]=0;
		printf("reveice message:%s\n",reqBuf);
	}
	close(sockfd);
	return 0;
}


After compiling, run the server first, and then open the client. The communication port is 9000. The
server on the left and the client on the right (write your own local IPv4 address, open CMD, and enter IPCONFIG to see)
Insert picture description hereInsert picture description here

Insert picture description hereThe client enters quit to exit. Then when the server listens to a new connection, the socket generated by the previous accept is closed. Start a new connection.

Reference: "Network Programming and Layered Protocol Design-Liu Biao"

Guess you like

Origin blog.csdn.net/qq_43179428/article/details/108831958