unix网络编程

1.基本TCP连接

1.1 基本TCP客户/服务器程序流程

在这里插入图片描述

1.2基本函数

根据连接流程图中顺序介绍:

  1. socket()
#include <sys/socket.h>
int socket (int family, int type, int protocol);
/*Returns: non-negative descriptor if OK, -1 on error*/
  • 作用:建立一个套接字,并返回一个描述符以方便使用。可以类比为创建了一个文件,使用文件标识符就可以操作文件。
  • 参数(family):“protocol family”,常用协议以下两种:
协议参数
IPv4AF_INET
IPv6AF_INET6
  • 参数(type):“type of socket”,常用类型以下两种:
类型参数
数据流SOCK_STREAM
数据报SOCK_DGRAM
  • 参数(protocol):“protocol of sockets”,传输协议类型,常用有以下三种:
传输协议参数
TCPIPPROTO_TCP
UDPIPPRTO_UDP
SCTPIPPROTO_SCTP
0由family和type确定的默认值

最常用的参数组合为: socket(AF_INET,SOCK_STREAM,0)

  1. bind()
#include <sys/socket.h>
int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
//Returns: 0 if OK,-1 on error
  • 作用:一个完整的套接字应该包括套接字种类、IP地址、端口号(客户端无需指定)。套接字种类由socket()指定,IP地址和端口号则由bind()确定。
  • 参数(sockfd):由Socket()创建的套接字描述符,指向该套接字。
  • 参数(const struct sockaddr *myaddr),传入协议指定的地址参数(包括IP地址、端口号等)。注意该参数定义的结构体为通用地址结构,即可以兼容多种协议地址格式,每种特定协议的地址结构体传入时需做强制类型转换。
/*通用套接字地址结构体:*/
struct sockaddr {
uint8_t 	sa_len;/*length of structure*/
sa_family_t sa_family; /*address family:AF_xxx */
char	    sa_data[14]; /*protocol-specific address*/

/*IPv4套接字结构体*/
struct in_addr{
in_addr_t 	s_addr; /*32-bit IPv4 address (network byte ordered)*/
}
struct sockaddr_in {
uint8_t 		sin_len;/*length of structure [16] */
sa_family_t 	sin_family;/*AF_INET*/
in_port_t		sin_port;/*16-bit port number(network byte ordered)*/
struct	in_addr sin_addr;/设置为全0,则监听该服务器的所有网卡(IP)/ 
char			sin_zero[8];/*unused*/
}
  1. listen()
#include <sys/socket.h>
#int listen (int sockfd, int backlog);
//Returns: 0 if OK, -1 on error
  • 作用:将套接字转变为被动接受状态,指示内核接受指向该套接字的连接,将接收到的连接放入队列中,等候accept。
  • 参数(backlog):接受连接队列的最大长度,即连接的最大个数。
  1. accept()
#include <sys/socket.h>
int accept (int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
//Returns: non-negative descriptor if OK, -1 on error
  • 作用:调用TCP服务并返回已完成连接队列中第一个连接;如果已连接的队列为空,则该进程被置为休眠状态。如果accept成功,返回一个代表该TCP连接的描述符。
  • 参数(struct sockaddr *cliaddr):客户端地址结构,从内核传输到进程。如不需要客户端地址信息可置为空,不影响连接及数据传输。
  • 参数(socklen_t *addrlen):值-结果(value-result)参数;有两个作用,一是传入地址结构体的大小,二是传出实际地址结构大小。采用指针是便于作用二的实现,即参数传出。如不需要客户端地址信息可置为空。
  • 注:Socket创建的接口描述符也称为监听套接字,一个服务器通常只创建一个监听套接字,可接受多个连接。accept创建的接口描述符也被称为连接套接字,内核为每个连接创建一个连接套接字,当连接结束时该连接套接字也被关闭。
  1. read()/readn() 与 write()/writen()
//这两个函数使用方法一样,传入参数:(连接描述符,接收数据地址,接收数据个数),返回值为实际接收的数据个数。
read (int __fd, void *__buf, size_t __nbytes);//库函数
readn(int fd, void *vptr, size_t n);//unix网络编程定义

//这两个函数使用方法一样,传入参数:(连接描述符,发送数据地址,发送数据个数),返回值为实际发送的数据个数。
write (int __fd, const void *__buf, size_t __n);//库函数
writen(int fd, const void *vptr, size_t n);//unix网络编程定义

字节流套接字上的read/write函数不同于文件io中的read函数,由于内核中套接字缓冲区的原因,在字节流上套接字上的read/write可能输出的字节数比请求的字节数少,需要多次调用read/write函数完成接收/发送数据。为了避免接收/发送数据时重复调用函数导致代码不整洁,unix网络编程书中定义了readn/writen函数,用于处理EINTR错误和重复调用read/write函数直至接收/发送完成。readn和writen函数定义如下:

readn(int fd, void *vptr, size_t n){
	size_t	nleft;
	ssize_t	nread;
	char	*ptr;
	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nread = read(fd, ptr, nleft)) < 0) {
			if (errno == EINTR)
				nread = 0;		/* and call read() again */
			else
				return(-1);
		} else if (nread == 0)
			break;				/* EOF */
		nleft -= nread;
		ptr   += nread;
	}
	return(n - nleft);		/* return >= 0 */
}
/* end readn */

writen(int fd, const void *vptr, size_t n)
{
	size_t		nleft;
	ssize_t		nwritten;
	const char	*ptr;
	ptr = vptr;
	nleft = n;
	while (nleft > 0) {
		if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
			if (nwritten < 0 && errno == EINTR)
				nwritten = 0;		/* and call write() again */
			else
				return(-1);			/* error */
		}
		nleft -= nwritten;
		ptr   += nwritten;
	}
	return(n);
}
/* end writen */
  1. close()
#include <unistd.h>
int close (int sockfd);
//Returns: 0 if OK, -1 on error

作用:在当前线程中关闭套接字,并对套接字的引用计数减一(当套接字引用计数为零时关闭连接)。

1.3 其它常用函数

  1. 地址转换函数
  • 网络字节序二进制地址形式转换为xxx.xxx.xxx.xxx形式(inet_ntop)
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
//Returns: pointer to result if OK, NULL on error
  • xxx.xxx.xxx.xxx地址形式转换为网络字节序二进制地址形式(inet_pton)
int inet_pton(int family, const char *strptr, void *addrptr);
//Returns: 1 if OK, 0 if input not a valid presentation format, -1 on error
//输出的二进制

2.网络字节顺序与本地字节顺序之间的转换函数

  • 主机转网络字节序 :
    htonl() – “Host to Network Long”
    htons() – “Host to Network Short”
  • 网络转主机字节序
    ntohl() – “Network to Host Long”
    ntohs( )-- “Network to Host Short”

2. 多线程(并发)

2.1基本函数

  1. fork()
#include <unistd.h>
pid_t fork(void);
//Returns: 0 in child, process ID of child in parent, -1 on error

参考资料

  • 作用:创建一个和原进程几乎一样的子进程。
  • 示例:
#include <unistd.h>  
#include <stdio.h>   
int main ()   
{   
    pid_t childpid; //fork函数返回值:在父进程中是子进程号;在子进程中为0;若为-1则代表fork失败。  
    int count=0;  
    childpid=fork();   
    if (childpid < 0)   
    {
        printf("error:can't fork!");  
    } 
    else if (childpid == 0) 
        {  
            printf("I'm the child process, my process id is %d\n",getpid());   
            printf("childpid=%d\n",childpid);
            count++;  
        }  
        else 
        {  
            printf("I'm the parent process, my process id is %d\n",getpid());   
            printf("childpid=%d\n",childpid);
            count++;  
        }  
    printf("程序运行计数count= %d\n",count);  
    return 0;  
}  
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值