zoukankan      html  css  js  c++  java
  • Linux网络编程——TCP和UDP通信

    • TCP协议流程图、TCP建立即时聊天
    • TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天
    • UDP协议流程图、UDP建立即时连接
    • 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。
    • SO_RCVLOWAT设置接收缓冲区下限

    1、TCP协议的流程图

    服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close
    客户端:socket----------------------------------connect---send---recv-----------------close

    1556806258606

    TCP建立即时聊天

    tcp_client.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd=socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd,-1,"socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family=AF_INET;
    	ser.sin_port=htons(atoi(argv[2]));
    	ser.sin_addr.s_addr=inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret=connect(socketFd,(struct sockaddr*)&ser,sizeof(ser));
    	ERROR_CHECK(ret, -1, "connect");
    	printf("connect success
    ");
    	char buf[128]={0};
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(socketFd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(socketFd, buf ,strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(socketFd);
    }
    

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    2、TCP即时聊天升级:服务器在客户端断开后不断开,客户端可以多次重连服务器进行即时聊天

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	char buf[128] = {0};
    	fd_set rdset; 
    	fd_set needMonitorSet; //需要监听的描述符集合
    	FD_ZERO(&needMonitorSet);  
    	FD_SET(STDIN_FILENO, &needMonitorSet);
    	FD_SET(socketFd, &needMonitorSet);
    	while(1){
    		memcpy(&rdset, &needMonitorSet, sizeof(fd_set));
    		ret = select(11, &rdset, NULL, NULL, NULL); //设最大监控描述符为10
    		if(FD_ISSET(socketFd, &rdset)){ //如果监听到客户端则accept接受远程计算机的连接请求
    			new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen); //accept函数接受一个连接时,会返回一个新的socket标识符,以后数据的数据传输和读取就要通过这个新的socket编号来处理,原来的socket继续监听其他客户机的连接请求。
    			ERROR_CHECK(new_fd, -1, "accept");
    			printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    			FD_SET(new_fd, &needMonitorSet);	
    		}
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				FD_CLR(new_fd, &needMonitorSet);  //从needMonitorSet中删除new_fd
    				close(new_fd);
    				continue;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

    3、使用UDP协议的流程图

    服务端:socket---bind---recvfrom---sendto---close
    客户端:socket----------sendto---recvfrom---close

    1556806183309

    • sendto()函数原型:
      **int sendto(int sockfd, const void msg,int len,unsigned int flags,const struct sockaddr to, int tolen);

      该函数比send()函数多了两个参数,to表示目地机的IP地址和端口号信息,而tolen常常被赋值为sizeof (struct sockaddr)。sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。

    • recvfrom()函数原型:

      **int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr from,int fromlen);
      from是一个struct sockaddr类型的变量,该变量保存连接机的IP地址及端口号。fromlen常置为sizeof (struct sockaddr)。当recvfrom()返回时,fromlen包含实际存入from中的数据字节数。Recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。

      UDP传输一个数据报示例:

      udp_server.c

    #include <func.h>
    
    int main(int argc, char **argv){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	char buf[128] = {0};
    	struct sockaddr_in client;
    	int addrlen = sizeof(client);
    	ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(ret, -1, "recvfrom");
    	printf("client ip = %s, port = %d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	printf("udp server gets %s
    ", buf);
    	//bzero(buf, sizeof(buf));
    	//ret = recvfrom(socketFd, buf, 5, 0, (struct sockaddr*)&client, &addrlen);
    	//ERROR_CHECK(ret, -1, "recvfrom");
    	//printf("udp server gets %s
    ", buf);
    	ret = sendto(socketFd, "world", 5, 0, (struct sockaddr*)&client, sizeof(client));
    	ERROR_CHECK(ret, -1, "sendto");
    	close(socketFd);
    	return 0;
    }
    

    udp_client.c

    #include <func.h>
    
    int main(int argc, char *argv[]){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);
    	int ret;
    	ret = sendto(socketFd, "helloworld", 10, 0, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "sendto");
    	char buf[128] = {0};
    	ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL);
    	ERROR_CHECK(ret, -1, "recvfrom");
    	printf("udp client gets %s
    ", buf);
    	close(socketFd);
    	return 0;
    }
    

    4、使用UDP协议建立即时连接

    udp_server.c

    #include <func.h>
    
    int main(int argc, char **argv){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser,sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	int ret;
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	char buf[128] = {0};
    	struct sockaddr_in client;
    	int addrlen = sizeof(client);
    	printf("client ip = %s, port = %d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recvfrom(socketFd, buf, sizeof(buf), 0, (struct sockaddr*)&client, &addrlen);
    			ERROR_CHECK(ret, -1, "recvfrom");
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(0 == ret){
    				printf("byeybye
    ");
    				break;
    			}
    			ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&client, sizeof(client));
    			ERROR_CHECK(ret, -1, "sendto");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    udp_client.c

    #include <func.h>
    
    int main(int argc, char *argv[]){
    	ARGS_CHECK(argc, 3);
    	int socketFd;
    	socketFd = socket(AF_INET, SOCK_DGRAM, 0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);
    	int ret;
    	char buf[128] = {0};
    	fd_set rdset;
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(socketFd, &rdset);
    		ret = select(socketFd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(socketFd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recvfrom(socketFd, buf, sizeof(buf), 0, NULL, NULL);
    			ERROR_CHECK(ret, -1, "recvfrom");
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(0 == ret){
    				printf("byeybye
    ");
    				break;
    			}
    			ret = sendto(socketFd, buf, strlen(buf) - 1, 0, (struct sockaddr*)&ser, sizeof(ser));
    			ERROR_CHECK(ret, -1, "sendto");
    		}
    	}
    	close(socketFd);
    	return 0;
    }
    

    5、如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)需调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket,但也可以直接用setsockopt和reuse。

    int reuse=1;
    setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&reuse,sizeof(int));
    

    tcp_server.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	int ret;
    	int reuse = 1;
    	ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    	ERROR_CHECK(ret, -1, "setsockopt");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

    6、SO_RCVLOWAT:接收缓冲区下限

    tcp_server_rcvlowat.c

    #include <func.h>
    
    int main(int argc,char* argv[])
    {
    	ARGS_CHECK(argc,3);
    	int socketFd;
    	socketFd = socket(AF_INET,SOCK_STREAM,0);
    	ERROR_CHECK(socketFd, -1, "socket");
    	int ret;
    	int reuse = 1;
    	ret = setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
    	ERROR_CHECK(ret, -1, "setsockopt");
    	struct sockaddr_in ser;
    	bzero(&ser, sizeof(ser));
    	ser.sin_family = AF_INET;
    	ser.sin_port = htons(atoi(argv[2]));
    	ser.sin_addr.s_addr = inet_addr(argv[1]);//点分十进制转为32位的网络字节序
    	ret = bind(socketFd, (struct sockaddr*)&ser, sizeof(ser));
    	ERROR_CHECK(ret, -1, "bind");
    	listen(socketFd, 10);//缓冲区的大小,一瞬间能够放入的客户端连接信息
    	int new_fd;
    	struct sockaddr_in client;
    	bzero(&client, sizeof(client));
    	int addrlen = sizeof(client);
    	new_fd = accept(socketFd, (struct sockaddr*)&client, &addrlen);
    	ERROR_CHECK(new_fd, -1, "accept");
    	printf("client ip=%s, port=%d
    ", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
    	fd_set rdset;
    	char buf[128] = {0};
    	int rcvLowAt = 10;
    	ret=setsockopt(new_fd,SOL_SOCKET,SO_RCVLOWAT,&rcvLowAt,sizeof(int));
    
    	ERROR_CHECK(ret, -1, "setsockopt");
    	while(1){
    		FD_ZERO(&rdset);
    		FD_SET(STDIN_FILENO, &rdset);
    		FD_SET(new_fd, &rdset);
    		ret = select(new_fd + 1, &rdset, NULL, NULL, NULL);
    		if(FD_ISSET(new_fd, &rdset)){
    			bzero(buf, sizeof(buf));
    			ret = recv(new_fd, buf, sizeof(buf), 0);
    			ERROR_CHECK(ret, -1, "recv");
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			printf("%s
    ", buf);
    		}
    		if(FD_ISSET(STDIN_FILENO, &rdset)){
    			memset(buf, 0, sizeof(buf));
    			ret = read(STDIN_FILENO, buf, sizeof(buf));
    			if(ret == 0){
    				printf("byebye!
    ");
    				break;
    			}
    			ret = send(new_fd, buf, strlen(buf) - 1, 0);
    			ERROR_CHECK(ret, -1, "send");
    		}
    	}
    	close(new_fd);
    	close(socketFd);
    	return 0;
    }
    

    tcp_client.c和1同

  • 相关阅读:
    POJ 2018 二分
    873. Length of Longest Fibonacci Subsequence
    847. Shortest Path Visiting All Nodes
    838. Push Dominoes
    813. Largest Sum of Averages
    801. Minimum Swaps To Make Sequences Increasing
    790. Domino and Tromino Tiling
    764. Largest Plus Sign
    Weekly Contest 128
    746. Min Cost Climbing Stairs
  • 原文地址:https://www.cnblogs.com/Mered1th/p/10803634.html
Copyright © 2011-2022 走看看