unix网络编程笔记三

accept函数

#include<sysy/socket.h>
int socket(int family,int type,int protocol);

使用这个函数创建一个套接字,函数执行成功返回一个套接字描述符,套接字描述符是一个小的整形数据,第一个参数表示地址族,第二个参数为套接字类型,第三个参数使用0来自适应前两个参数。

family的取值

  • AF_INET表示IPv4
  • AF_INET6表示IPv6
  • AF_LOCAL表示unix域协议
  • AF_ROUTE表示路由协议
  • AF_KEY表示秘钥协议

type的取值

  • SOCK_STREAM表示流式套接字
  • SOCK_DGRAM表示数据报套接字
  • SOCK_RAW表示原始套接字

函数成功返回文件描述符,失败返回-1。

connect函数

#include<sys/socket.h>
int connect(int sockfd,struct sockaddr *seraddr,socklen_t addrlen);
										函数执行成功返回0,失败返回-1

如果使用的是TCP协议,则这个函数的执行动作是三次握手,主动和服务器进行连接,这个函数是阻塞的,当成功或者出错时返回。

当是一个TCP连接会出现以下几种错误情况

  1. 返回ETIMEDOUT错误,这个错误表示没有收到服务器端对SYN的响应,当客户端向服务器发送SYN时没有收到回应便会隔一段时间再发,如果每次都没有回应,这个函数便会返回这个错误,表示建立连接超时。
  2. 返回ECONNREFUSED错误,这个错误表示当客户端发送SYN时,收到的回应为RST,表示建立连接被拒绝,原因是服务器没有在指定的端口上监听。

产生RST的几个原因

  1. 目的端口上接收到SYN时,但是该端口上没有开启监听服务便会返回一个RST。
  2. TCP想要取消一个已连接时。
  3. 当一个没有连接的数据包到达时。

RST包的作用

  1. 表示强制关闭连接

bind函数

#include<sys/socket.h>
int bind(int sockfd,struct sockaddr *myaddr,socklen_t addrlen);
											成功返回0,失败返回-1

这个函数的作用便是给指定的套接字绑定地址。

bind函数通常使用在服务器端,给一个服务器绑定ip和端口号,表示接收目的地址为特定ip和端口号的数据包。

客户端可以不绑定ip和端口号,会随机使用自身的ip地址和端口号,如果自身有多个ip也可以绑定的定的ip地址和端口号来作为数据包的源ip和源端口号。

服务器绑定ip可以使用通配地址INADDR_ANY表示服务器监听所有的ip地址。

listen函数

#include<sys/socket.h>
int listen(int sockfd,int backlog)
				成功返回0,失败返回-1

这个函数做了两件事

  • 将sockfd套接字设置为监听套接字,能够接受目的地址指向这个套接字的数据包。
  • 设置了最多连接个数。

监听套接字维护两个队列

在这里插入图片描述

  • 未完成连接的队列:表示已经接收到客户端的SYN处于SYN_RCVD状态,但是三次握手还没有完成的客户端。
  • 已完成连接的队列:表示已经完成连接的客户。

accept函数

#include<sys/socket.h>
int accept(int listenfd,struct sockaddr *cliaddr,socklen_t addrlen);
										成功返回新的套接字描述符,失败返回-1

这个函数的作用便是从已连接的队列里返回一个连接,当已完成连接队列里没有时这个函数会阻塞。

第一个参数表示监听套接字的文件描述符,第二个参数表示希望得到客户端地址的结构,第三个表示地址的长度,是一个值—结果参数,当不客户端的地址时可以将最后两个参数设置为NULL。

客户端和服务端的程序

使用一个例子来使用一下上面的函数,下面是一个服务器读取客户端ip地址和端口号的例子。

server

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#define port 1234
int main()
{
int er;
int listenfd,newfd;
struct sockaddr_in seraddr,cliaddr;
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(port);
seraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
        printf("socket error\n");
        return 0;
}
if((er=bind(listenfd,(struct sockaddr *)&seraddr,sizeof(seraddr)))==-1)
{
        printf("bind error\n");
        return 0;
}
if((er=listen(listenfd,5))==-1)
{
        printf("listen error\n");
        return 0;
}
while(1)
{

int len=sizeof(cliaddr);
if((newfd=accept(listenfd,(struct sockaddr *)&cliaddr,&len))==-1)
{
        printf("accept error\n");
        return 0;
}
printf("client ip address is %s\nclient port is %d\n\n\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
close(newfd);
}

}

client

#include<string.h>
#include<netinet/in.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/socket.h>
#define port 1234
int main()
{
int clifd,er;
struct sockaddr_in seraddr;
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(port);
seraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
if((clifd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
        printf("socket error\n");
        return 0;
}
if((er=connect(clifd,(struct sockaddr *)&seraddr,sizeof(seraddr)))==-1)
{
        printf("connect error\n");
        return 0;
}
close(clifd);
}

开启服务器和客户机程序

root@ubuntu:/home/sun/project2# ./server
client ip address is 127.0.0.1
client port is 33570


client ip address is 127.0.0.1
client port is 33572


client ip address is 127.0.0.1
client port is 33574


client ip address is 127.0.0.1
client port is 33576


client ip address is 127.0.0.1
client port is 33578

fork函数和exec类函数

#include<unistd.h>
pid_t fork(void);
子进程的返回是0,父进程返回子进程的id,错误返回-1

这个函数用来创建一个子进程,运行一个返回两次,子进程是父进程的副本,共享在fork之前的文件描述符,这个函数有两种用途。

  1. 父进程创建一个子进程使子进程和父进程完成不同的工作,比如服务器一个进程监听用户的连接,当用户发出连接时调用fork函数,创建一个进程来和客户端进程交互。
  2. 调用fork程序后使用exec来调用其他程序。

高并发服务器

上面那个例子中因为accept是阻塞的因此同事只能有一个client和server连接,这种方式是很低效的,而高并发的一个思想是同时可以有多个client和这个server连接,实现的方法便是使用fork函数,将连接的client和fork创建的子进程交互。

下面是UNIX网络编程中显示过程的图片
在这里插入图片描述

在这里插入图片描述在这里插入图片描述
我们来实现一个高并发的服务器
server1

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<unistd.h>
#include<arpa/inet.h>
#define port 1234
int main()
{
int er;
int listenfd,newfd;
struct sockaddr_in seraddr,cliaddr;
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(port);
seraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
        printf("socket error\n");
        return 0;
}
if((er=bind(listenfd,(struct sockaddr *)&seraddr,sizeof(seraddr)))==-1)
{
        printf("bind error\n");
        return 0;
}
if((er=listen(listenfd,5))==-1)
{
        printf("listen error\n");
        return 0;
}
while(1)
{

int len=sizeof(cliaddr);
if((newfd=accept(listenfd,(struct sockaddr *)&cliaddr,&len))==-1)
{
        printf("accept error\n");
        return 0;
}
if((er=fork())<0)
{
        printf("fork error\n");
        return 0;
}
if(er==0)    //childen process
{
        close(listenfd);  //childen process close listenfd
        write(newfd,"hello",sizeof("hello"));
        while(1);
        return 0;         //childen end 
}
close(newfd);  //father process close newfd
}
}

client1

#include<string.h>
#include<netinet/in.h>
#include<string.h>
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<sys/socket.h>
#define port 1234
int main()
{
int clifd,er;
struct sockaddr_in seraddr;
seraddr.sin_family=AF_INET;
seraddr.sin_port=htons(port);
seraddr.sin_addr.s_addr=inet_addr("127.0.0.1");
if((clifd=socket(AF_INET,SOCK_STREAM,0))==-1)
{
        printf("socket error\n");
        return 0;
}
if((er=connect(clifd,(struct sockaddr *)&seraddr,sizeof(seraddr)))==-1)
{
        printf("connect error\n");
        return 0;
}
char buf[100];
read(clifd,buf,100);
printf("%s\n",buf);
while(1);
close(clifd);
}

开启server1和同时开启几个client1可以看到每个client1都会收到hello的消息。

root@ubuntu:/home/sun/project2# ./client1
hello
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值