tcp网络编程
tcp网络编程步骤:
由于tcp传输特点是可靠有连接,那么就有
1.客户端向服务端发送连接请求(SYN),
2.服务端接受请求并向客户端发送(SYN+ACK);
3.客户端向服务端回复ACK表明他知道服务端同意连接。
以上三个步骤就是三次握手。
服务端编程步骤:
1.创建套接字
2.为套接字绑定地址信息
3.监听:开始接受服务端的连接请求
4.获取连接建立成功的新socket
5.发送数据
6.接受数据
1.创建套接字

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:地址域AF_INET :ipv4协议
type: 套接字类型SOCK_STREAM 流式套接字SOCK_DGRAM  数据报套接字
protocol :协议类型 如果是0,则表示默认;流式套接字默认tcp协议,报式套接字默认udp协议流式套接字: IPPROTO_TCP 6 报式套接字:IPPROTO_UDP 17
如:socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
返回值:成功:套接字描述符 失败:-1

2.为socket绑定地址信息

 #include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);参数: sockfd: socket描述符addr :socket绑定的地址addrlen :地址信息长度返回值:成功:0(网卡操作那个进程),失败 -1功能:将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。sockaddr结构:struct sockaddr {sa_f  amily_t sa_family;char        sa_data[14];}
虽然bind里参数是sockaddr,但是真正在基于IPV4编程时,使用的结构体是sockaddr_in;这个结构体里主要有三部分信息:地址类型,端口号,IP地址。
sockaddr_in在头文件#include<netinet/in.h>或#include<arpa/inet.h>中定义。该结构体解决了sockaddr的缺陷,把port和addr 分开储存在两个变量中,如下:
structsockaddr_in{short           sin_family;//AF_INET(地址族)PF_INET(协议族)unsigned short  sin_port;/*Portnumber(必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/struct in_addr  sin_addr;//32位IP地址unsigned char   sin_zero[8];//没有实际意义,只是为了跟SOCKADDR结构在内存中对齐*/};
该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址:
typedef uint32_t in_addr_t;
struct in_addr
{in_addr_t s_addr;
};
in_addr用来表示一个IPV4的IP地址,其实是一个32位整数。

客户端不推荐手动绑定地址信息 ,因为绑定有可能因为特殊原因失败,但是客户端具体使用哪个地址和端口都可以,只要能把数据发送出去,所以客户端程序不手动绑定地址,直至发送数据时,操作系统检测到socket没有绑定地址,会自动选择合适的地址和端口为socket绑定地址,这种数据一般不会出错。
3.监听(服务端监听后才可以接受客户端连接请求)

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);listen()声明sockfd处于监听状态,并且最多允许backlog个客户端处于连接等待状态,如果接受到更多的连接请求就忽略,一般是5,即代表最大同时并发连接数为5,这个数字并不是tcp最大建立连接数。(文章后面会讲述tcp最大建立连接数)
返回值:成功: 0  失败 -1

4.accept():获取新建立的socket

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd : socket描述符
addr  :新建立连接的客户端地址信息
addrlen :地址信息长度
返回值:成功:返回新的socket连接描述符失败:-1
accept是阻塞型函数,如果连接成功的队列没有新的连接,将会一直阻塞等待新的客户端连接

参数sockfd和返回值newsockfd区别:
sockfd :所有连接请求的数据发送到socket这个缓冲区(包括服务端ip和port),然后进行处理(为这个新建立连接的客户端新建立一个socket);
newsockfd: 连接建立成功后,连接成功的客户端发送的数据都发送到这个新的socket缓冲区(包括服务端ip port和建立连接客户端ip port)。
5.发送数据

 #include <sys/types.h>#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);flag :  0默认阻塞发送数据

由于accept返回的socket描述符中有客户端ip和port,所以参数中就没有struct sockaddr_in 和 addrlen,这是和udp发送数据的区别。同理,tcp和udp接受数据函数参数不同。

6.接受数据

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
sockfd: 里面已经包含从哪儿接受数据信息,是新的sockfd
buf:用于接受数据
len :用于接受数据长度
flags:  0  默认 阻塞式接收
返回值 : 错误 : -1连接关闭 ; 0实际接受数据 >0

7.关闭socket描述符

要在任意可能退出的地方关闭对应的socket描述符。
tcp服务端代码

// tcp 服务端代码
//1.创建套接字
//2.绑定地址信息
//3.监听:监听之后获取新的socket连接
//4.获取新的socket连接
//5.接受数据
//6.发送数据
//7.关闭socket描述符
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>int main(int argc,char *argv[]) //将需要绑定的IP地址和port在命令行输出来
{if(argc!=3){   printf("Usage:ip and port\n");}   //1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sockfd<0){   perror("sockfd error");return -1; }   //2.绑定地址信息// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);struct sockaddr_in ser_addr;ser_addr.sin_family=AF_INET;ser_addr.sin_addr.s_addr=inet_addr(argv[1]);ser_addr.sin_port=(htons)(atoi(argv[2])); int len=sizeof(struct sockaddr_in);int ret=bind(sockfd,(struct sockaddr*)&ser_addr,len);if(ret<0){perror("binf error");close(sockfd);return -1;}//3.监听// int listen(int sockfd, int backlog);if(listen(sockfd,5)<0)//开始监听,接受客户端的连接请求,最大同时并发连接数为5{perror("listen error");close(sockfd);return -1;}//连接建立成功后,服务端会新建立一个socketwhile(1){  //用while循环当一个连接断开后,可以重新获取新的socket//4.获取新建立的socketstruct sockaddr_in cli_addr;len=sizeof(struct sockaddr_in);// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);int newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);//获取成功,返回新的socket描述符if(newsockfd<0){perror("newsockfd error");return -1;}//连接建立成功:printf("new con:%s %d\n",(inet_ntoa)(cli_addr.sin_addr),ntohs(cli_addr.sin_port));while(1)  //用循环是保证服务端可以和一个客户端可以多次聊天{//5.发送数据 //tcp协议:获取新的socket描述符后,新的socket里包含了服务端和客户端的地址信息,所以发送和接受数据没有先后之分// ssize_t send(int sockfd, const void *buf, size_t len, int flags);char buff[1024]={0};printf("please send data:");scanf("%s",buff);ret=send(newsockfd,buff,strlen(buff),0); //阻塞发送数据 if(ret<0){perror("send error");close(newsockfd);return -1;}//6.接受数据//ssize_t recv(int sockfd, void *buf, size_t len, int flags);memset(buff,0x00,1024);len=recv(newsockfd,buff,1023,0);//0默认阻塞接受数据if(len<0)//小于0接受失败{perror("recv error");close(newsockfd);continue;}else if(len==0)//等于0对端将连接断开{perror("peer has performed an orderly shutdown");close(newsockfd);continue;}printf("[%s:%d]->%s\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),buff);}close(newsockfd);}close(sockfd);return -1;
}

tcp客户端编程步骤
1.创建套接字
2.绑定地址信息(没有必要调用bind()绑定信息,否则一台机器上启动多个客户端,就会出现端口号被占用而导致不能正常连接)
3.向服务端发起连接请求
4.接受数据
5.发送数据
6.关闭
创建套接字、发送数据、接受数据即挂壁和服务端一样。
3.向服务端发送连接请求

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
addr:要连接的服务端地址
addrlen :地址信息长度
返回值: 成功; 0  失败 -1

客户端代码:

//tcp 客户端代码
//1.创建套接字
//2.绑定地址信息
//3.向服务端发送连接请求
//4.发送数据
//5.接受数据
//6.关闭socket描述符#include<stdio.h>
#include<error.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/socket.h>
#include<stdlib.h>int main(int argc,char* argv[])
{if(argc!=3){   printf("Usage ip and port\n");}   //1.创建套接字int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(sockfd<0){   perror("socket error");return -1; }    //2.绑定地址信息(不推荐手动写绑定信息代码)//3.向服务端发送连接请求//int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);struct sockaddr_in ser_addr;ser_addr.sin_family=AF_INET;ser_addr.sin_port=(htons)(atoi(argv[2]));  //htons :主机字节序转换成网络字节序ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);//因为argv[]是char*,用atoi使字符串转成整型int len=sizeof(struct sockaddr_in);
int ret=connect(sockfd,(struct sockaddr*)&ser_addr,len);if(ret<0){perror("connect error");close(sockfd);return -1;}//连接成功,socket描述符里有服务端和客户端IP地址和portwhile(1){//4.接受数据//ssize_t recv(int sockfd, void *buf, size_t len, int flags);char buff[1024]={0};ret=recv(sockfd,buff,1023,0);//默认阻塞接受数据if(len<0)//小于0接受失败{perror("recv error");close(sockfd);continue;}else if(len==0)//等于0对端将连接断开{perror("peer has performed an orderly shutdown");close(sockfd);continue;}// net_ntoa :网络字节序转换成点分十进制IP//ntohs  :主机字节序转换成网络字节序printf("[%s:%d]say:%s\n",(inet_ntoa)(ser_addr.sin_addr),(ntohs)(ser_addr.sin_port),buff);//4.发送数据// ssize_t send(int sockfd, const void *buf, size_t len, int flags);memset(buff,0x00,1024);printf("please send\n");scanf("%s",buff);ret=send(sockfd,buff,strlen(buff),0); //默认阻塞接受数据if(ret<0){perror("send error");close(sockfd);return -1;}}close(sockfd);return 0;
}

客户端:

服务端:

当客户端ctrl+c断开连接后,服务端会提示对端已关闭,这时会有新的客户端建立连接。
listen参数和tcp最多建立连接数
int listen(int sockfd, int backlog);
backlog:
协议栈使用一个队列:这个队列的大小由listen系统调用的backlog参数决定。当一个syn包到达后,服务端协议栈回复syn+ack,然后将这个socket加入这个队列。当客户端第三次握手的ack包到达后,再将这个socket的状态改为ESTABLISHED状态。这也就意味着这个队列可以可以容纳两种不同状态的socket:SYN RECEIVED和 ESTABLISHED,而只有后者可以被accept调用返回。当队列中的连接数(socket)达到backlog个后,系统收到syn将不再回复syn+ack。这种情况下协议栈通常仅仅是将syn包丢掉,而不是回复rst报文,从而让客户端可以重试。
tcp最大连接数:
用ulimit -n 结果是1024,这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件需要除去每个进程必然打开的标准输入、标准输出、标准错误、服务器监听socket,进程间通讯的unix域socket等文件,那么剩下可用于客户端socket连接的文件数就只有大概1024-10=1014个,即在缺省条件下,基于linux的通讯程序最多允许同时1014个TCP并发连接。

tcp网络编程客户端和服务端及listen和tcp允许最大连接数相关推荐

  1. Python网络编程——客户端与服务端简单信息发送与接受

    python网络编程-服务端与客户端简单信息发送与接受 思考一下我们进行网络中信息通信都需要什么或者说需要具备什么条件才能进行网络通信? 首先我们需要知道给谁发送,也就是目标机是谁,然后我们需要知道对 ...

  2. QT实现网络编程---客户端、服务端

    客户端: Client.h #pragma once #include <QtWidgets/QWidget> #include "ui_Client.h" #incl ...

  3. java 网络编程(二) tcp传输实现客户端和服务端进行信息交流

    1.使用Tcp从一台电脑往另一台电脑上发送文本数据 客户端: import java.io.*; import java.net.*; /**** 客户端,* 通过查阅socket对象,发现在该对象建 ...

  4. TCP/IP网络编程之多进程服务端(一)

    TCP/IP网络编程之多进程服务端(一) 进程概念及应用 我们知道,监听套接字会有一个等待队列,里面存放着不同客户端的连接请求,如果有一百个客户端,每个客户端的请求处理是0.5s,第一个客户端当然不会 ...

  5. TCP/IP网络编程之多进程服务端(二)

    TCP/IP网络编程之多进程服务端(二) 信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非 ...

  6. 【学习笔记】在windows下进行基于TCP的本地客户端和服务端socket通信

    文章目录 socket介绍 java中使用socket 基于tcp的socket通信 使用ServerSocket类创建一个web服务器:(java) windows下的基于tcp的socket编程( ...

  7. C++网络编程学习:服务端多线程分离业务处理高负载

    网络编程学习记录 使用的语言为C/C++ 源码支持的平台为:Windows / Linux 笔记一:建立基础TCP服务端/客户端  点我跳转 笔记二:网络数据报文的收发  点我跳转 笔记三:升级为se ...

  8. python交互式编程客户端_【python】UDP网络编程:实现服务端与客户端的交互、简单的AI智能模式...

    关于UDP网络编程 UDP(user datagram protocol)的中文叫用户数据报协议,属于传输层.UDP是面向非连接的协议,它不与对方建立连接,而是直接把要发的数据发给对方. [UDP网络 ...

  9. linux的tcp非阻塞客户端与服务端demo源码

    客户端#include <stdio.h> #include <string.h> #include <errno.h> #include <sys/sock ...

最新文章

  1. Confusion matrix
  2. 中达变频器参数_台达变频器:满足未来驱动需求
  3. Java中,一切皆是对象——java中的对象类型与基本数据类型的区别
  4. vs2005中关于masterpage,Theme,skin的一点总结
  5. Cloudstack系统配置(三)
  6. MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established
  7. 【Python】 子进程创建与使用subprocess
  8. mysql联合索引和单索引_mysql联合索引跟单列索引的区别
  9. MySQL自动化审核平台部署说明
  10. uilabe加载html,UILabel加载html字符串
  11. JDK Frame内容区绘制边框
  12. 活在迷茫中,不是活在当下
  13. 翻译:如何理解K-means的缺点
  14. pdf转word文档总结
  15. office2019初体验与kms服务器搭建
  16. Cisco Packet Tracer中配置单区域OSPF
  17. 测试工程师需掌握的技能
  18. 【多媒体编解码】Openmax IL (一)官方文档概述
  19. u盘启动Linux什么时候可以拔下来,u盘装系统什么时候拔u盘|pe重装系统什么时候拔掉u盘...
  20. 一本通 P1486 【黑暗城堡】

热门文章

  1. NV12转YUV420P
  2. 《初级会计电算化实用教程(金蝶ERP—K/3版)》一第1章 会计电算化概论1.1 会计电算化概念...
  3. 攻防世界-逆向game
  4. 地理信息系统(ArcGIS)在水文水资源、水环境中的实践技术应用及案例分析
  5. java企业工资管理系统_基于B/S模式下的JAVA工资管理系统
  6. robotframework:log日志输出现中文乱码如xC4xE3
  7. 如何利用VPS配置搭建frp/frps进行内网穿透
  8. html计算奇偶数,奇偶校验(even,odd,Parity Check)在线计算器_三贝计算网_23bei.com
  9. Google排名新算法之二
  10. CCS3.3 编译链接系列错误