参数:
domain–指定使用那个版本的协议进行通信-AF_INET(ipv4)
type–套接字类型:流式套接字;数据报套接字
protoc–协议类型:IPPROTO_TCP=6 ; IPPROTO_UDP=17
sockfd = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP);
//struct sockaddr_in //ipv4
//struct sockaddr_in6 //ipv6
int sockfd = -1;
sockfd = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP);
if(sockfd < 0)
{
perror("socket");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
//inet_addr---将字符串的IP地址转换为网络主机地址
addr.sin_addr.s_addr = inet_addr("192.168.122.131");
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("bind");
return -1;
}
char buff[1024] = {0};
struct sockaddr_in cli_addr;
socklen_t len = sizeof(struct sockaddr_in);
recvfrom(sockfd, buff, 1023, 0,
(struct sockaddr*)&cli_addr, &len);
//recvfrom返回实际接收的数据长度
//flag默认给0,阻塞式等待数据
//addr 获取客户端的地址
//&len获取的是实际的 信息长度
sendto(sockfd, tmp, strlen(tmp), 0, addr, len)
//返回实际发送的数据长度
//addr 将数据发送给addr这个地址
close(sockfd);
注:
inet_addr –将字符串的ip地址转换为网络主机地址
客户端不推荐手动绑定地址:绑定地址有可能会失败导致无法发送
关闭socket
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
//1.创建socket
int sockfd = -1;
sockfd = socket(AF_INET,SOCK_DGRAM, IPPROTO_UDP);
if(sockfd < -1)
{
perror("socket");
return -1;
}
//2.定义服务器地址
struct sockaddr_in serv_addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(9000);
addr.sin_addr.s_addr = inet_addr("192.168.2.2");
socklen_t len = sizeof(sockaddr_in);
//3.向server发送数据
while(1)
{
char buf[1024]={};
scanf("%s",&buf);
sendto(sockfd,buf,strlen(buf),0,(struct sockaddr*)&serv_addr,len);
//4.接收数据
char tmp[1024] = {0};
struct sockaddr_in addr;
recvfrom(sockfd, tmp, 1023, 0,
(struct sockaddr*)&addr, &len);
printf("serv say:%s\n", tmp);
}
//5.关闭socket
close(sockfd);
客户端 SYN ->服务端 客户端发起连接请求
客户端 <-ACK SYN 服务端 服务端发起连接并响应
客户端 ACK-> 服务端 客户端响应
4. 接收连接成功后的新socket(sip sport dip dport),对于每一个客户端都有自己的一个socket,每个客户端发送数据过来都有自己的一块新缓冲区用来存储数据;
5. 收发数据
6. 关闭 socket
连接成功队列:队列长度有限,同一时间的最大连接数,队列中取走一个,可以再加一个。
了解:泛洪攻击
header 1 |
---|
row 1 col 1 |
row 2 col 1 |
注意:如何判断TCP连接断开?
如果不想让进程退出,那么需要在网络程序运行初始化时对SIGPIPE信号做出自定义/忽略处理。
int main(int argc, char *argv[])
{
int lst_sockfd = -1;
if (argc != 3) {
printf("Usage: ./tcp_server ip port\n");
return -1;
}
//创建一个拉皮条的socket---监听socket
lst_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lst_sockfd < 0) {
perror("socket error");
return -1;
}
//为监听socket绑定一个监听的地址
struct sockaddr_in lst_addr;
lst_addr.sin_family = AF_INET;
lst_addr.sin_port = htons(atoi(argv[2]));
lst_addr.sin_addr.s_addr = inet_addr(argv[1]);
//inet_aton(argv[1], &lst_addr.sin_addr);
//inet_pton(AF_INET, argv[1], &lst_addr.sin_addr);
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(lst_sockfd, (struct sockaddr*)&lst_addr, len);
if (ret < 0) {
perror("bind error");
return -1;
}
//开始监听socket
if (listen(lst_sockfd, 5) < 0) {
perror("listen error");
return -1;
}
while(1) {
int new_fd;
struct sockaddr_in cli_addr;
//从已成功连接队列取出一个新连接
//假如这个队列里边没有新连接就会阻塞等待客户端的连接
new_fd = accept(lst_sockfd, (struct sockaddr*)&cli_addr,
&len);
if (new_fd < 0) {
perror("accept error");
continue;
}
//接收新连接客户端的数据
//这个新的连接里边已经定义好了数据通信的源地址端口和
//目的地址端口,因此在接收或发送数据的时候,就不需要
//重新确定了。
while(1) {
char buff[1024] = {0};
ssize_t r_len = recv(new_fd, buff, 1023, 0);
if (r_len == 0) {
printf("peer shutdown!!\n");
break;
}
printf("client:%s:%d say:%s\n",
inet_ntoa(cli_addr.sin_addr),
ntohs(cli_addr.sin_port),
buff);
/*
//向新连接的客户端回复数据
printf("server say:");
fflush(stdout);
char tmp[1024] = {0};
scanf("%s", tmp);
send(new_fd, tmp, strlen(tmp), 0);
*/
}
close(new_fd);
}
close(lst_sockfd);
return 0;
}
1.创建socket
socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
2.向服务器端发起连接
3.收发数据
4.关闭socket
void sigcb(int signo)
{
printf("recv a signal:SIGPIPE\n");
}
int main(int argc, char *argv[])
{
int sockfd = -1;
if (argc != 3) {
printf("Usage: ./tcp_client ip port\n");
return -1;
}
signal(SIGPIPE, sigcb);
//1. 创建socket
sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0) {
perror("socket error");
return -1;
}
//向服务器端发起连接
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(atoi(argv[2]));
serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(struct sockaddr_in);
int ret = connect(sockfd, (struct sockaddr*)&serv_addr, len);
if (ret < 0) {
perror("connect error");
return -1;
}
//连接建立成功之后,sockfd将确定下来,操作的数据应该从哪来,
//到哪去 ,所以收发数据,将不用再确定从哪发,发给谁
while(1) {
//向服务器端发送数据
//printf("client say:");
//fflush(stdout);
char buff[1024] = {0};
//scanf("%s", buff);
sprintf(buff, "%s", "zheshiyige ceshi shuju!!");
send(sockfd, buff, strlen(buff), 0);
sleep(1);
//接收服务器端的回复
/*
char tmp[1024] = {0};
recv(sockfd, tmp, 1023, 0);
printf("server say:%s\n", tmp);
*/
}
close(sockfd);
return 0;
}
void handle(int sockfd, struct sockaddr_in addr)
{
while(1) {
char buff[1024] = {0};
int ret = recv(sockfd, buff, 1023, 0);
if (ret <= 0) {
if (errno == EINTR || errno == EAGAIN) {
usleep(1000);
continue;
}
close(sockfd);
exit(0);
}
printf("client:[%s:%d] say:%s\n",
inet_ntoa(addr.sin_addr),
ntohs(addr.sin_port), buff);
send(sockfd, "i am robot~!!!", 14, 0);
}
close(sockfd);
return ;
}
int create_worker(int sockfd, struct sockaddr_in addr)
{
int pid = -1;
pid = fork();
if (pid < 0) {
close(sockfd);
return -1;
}else if (pid == 0) {
//启动子进程来处理于客户端的聊天
//如果子进程一直不退出,则父进程会一直等待他
if (fork() == 0) {
//而孙子进程处理完毕聊天后,会退出,资源被init进程回收
//因为它的父进程已经退出,它变成了孤儿进程
handle(sockfd, addr);
}
//让子进程直接退出,是为了让父进程的等待直接返回
exit(0);
}else {
//父进程记得关闭描述符,否则会占据资源不释放
//同时也害怕出现跟子进程同时操作的混乱问题
close(sockfd);
//为了防止子进程成为僵尸进程,而不释放资源
wait(NULL);
}
return 0;
}
int main(int argc, char *argv[])
{
int lst_fd = -1;
if (argc != 3) {
printf("Usage: ./ptcp_server ip port\n");
return -1;
}
lst_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lst_fd < 0) {
perror("socket error");
return -1;
}
struct sockaddr_in lst_addr;
lst_addr.sin_family = AF_INET;
lst_addr.sin_port = htons(atoi(argv[2]));
lst_addr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(lst_fd, (struct sockaddr*)&lst_addr, len);
if (ret < 0) {
perror("bind error");
return -1;
}
if (listen(lst_fd, 5) < 0) {
perror("listen error");
return -1;
}
while(1) {
int newfd = -1;
struct sockaddr_in cli_addr;
newfd = accept(lst_fd, (struct sockaddr*)&cli_addr, &len);
if (newfd < 0) {
perror("accept error");
continue;
}
create_worker(newfd, cli_addr);
}
return 0;
}
/*
* 基于TCP的一个多线程简单网络聊天程序
* 1. 在接收一个客户端新连接后启动一个线程机器人来聊天
*/
void* handle(void *arg)
{
int sockfd = (int)arg;
pthread_t tid = pthread_self();
while(1) {
char buff[1024] = {0};
int ret = recv(sockfd, buff, 1023, 0);
if (ret <= 0) {
break;
}
printf("client:%lu say: %s\n", tid, buff);
send(sockfd, "i don't konw!!", 14, 0);
}
close(sockfd);
return NULL;
}
int create_robot(int sockfd)
{
pthread_t tid;
pthread_create(&tid, NULL, handle, (void*)sockfd);
pthread_detach(tid);
return 0;
}
int main(int argc, char *argv[])
{
int lstfd = -1;
lstfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lstfd < 0) {
perror("socket error");
return -1;
}
struct sockaddr_in lst_addr;
lst_addr.sin_family = AF_INET;
lst_addr.sin_port = htons(atoi(argv[2]));
lst_addr.sin_addr.s_addr = inet_addr(argv[1]);
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(lstfd, (struct sockaddr*)&lst_addr, len);
if (ret < 0) {
perror("bind error");
return -1;
}
if (listen(lstfd, 5) < 0) {
perror("listen error");
return -1;
}
while(1) {
int newfd = -1;
struct sockaddr_in cli_addr;
newfd = accept(lstfd, (struct sockaddr*)&cli_addr, &len);
if (newfd < 0) {
perror("accept error");
continue;
}
create_robot(newfd);
}
return 0;
}
TCP三次握手过程
四次挥手过程
time_wait状态;2MSL
netstat -anptu
TCP和UDP的对比:
有连接 无连接
可靠的 不可靠的
字节流(内部数据可以拆分来发) 数据报(整体,不可拆分包)
////////////