linux网络套接口编程基础

1.主要的头文件:

1):sys/types.h 基本系统数据类型是Unix/Linux系统的基本系统数据类型的头文件,含有size_t,time_t,pid_t等类型。

2):netinet/in.h  Internet address family定义 sockaddr_in结构体,htons,htonl,ntohs, ntohl字节序列转换函数的头文件。

3):sys/socket.h 通用套接口地址结构sockaddr的头文件。

4):arpa/inet.h 定义地址转换函数,即ASCII字符串和网络字节序列之间的转换,inet_aton,inet_addr,inet_ntoa,对于IPV4和IPV6都通用的有inet_pton,inet_ntop函数。

5):sys/socket.h 定义socket,connect,bind,listen,accept等基本套接口函数

6):unistd.h定义fork, exec函数。

7):signal.h 定义signal函数。

8):string.h 定义bzero,memset,bcopy,memcpy等字符操作函数。

9):stdio.h 包含printf,scanf,snprintf,sscanf,fgets,fputs,read,write等函数。

10):stdlib.h 定义malloc,exit等函数。

2.主要结构体和函数:

1):网际套接口地址结构: sockaddr_in

struct in_addr {

in_addr_t s_addr;};

struct sockaddr_in {

uint8_t sin_len;

sa_family_t sin_family;

in_port_t sin_port;

struct in_addr sin_addr;

char sin_zero[8];};

2) :通用套接口地址(sys/socket.h头文件定义)sturct sockaddr

struct sockaddr {

unit8_t sa_len;

sa_family_t sa_family;

char sa_data[14];

};

3) :字节排序函数,主要原因是内存中存储字节的方式不同有大端法和小端法,系统用的字节序列称为主机字节序列,网络用的字节序列叫网络字节序列使用大端法,比如uint_16_t htons(uint16_t host16bitbalue)把主机字节序列转化成网16字节short类型网络字节序列,16字节一般用在端口号的转化上,而uint32_thtonl(uint32_t host32bitvalue)则是把主机字节序列转化成32字节整性的网络字节序列,返回值即网络字节序列。

4)地址转化函数在ASCII字符串和网络字节序列的二进制见转化。 如:int inet_aton(const char*strptr, sruct in_addr *addrptr

将strptr所指向的C字符串转化成32位网络字节序二进制值,并用addrptr来存储,成功返回1,否则返回0.

in_addr_t inet_addr(const char *strptr);返回32位网络字节序二进制的值,而char * inet_ntoa(struct in_addr inaddr)则是将32位网路字节序2进制转化成相应的点分十进制。

inet_pton 和inet_ntop函数则是同样的功能不过对于IPV6同样适用。

3.套接口API

1)socket函数:int socket(int family, int type, int protocol),family参数指明协议族,一般为AF_INET,type指明套接口类型对于TCP协议一般用SOCK_STREAM,最后一个参数通常设置为0.

2)connect函数:int connect(int sockfd, const struct sockaddr *servaddr,socklen_t addrlen);第一个参数通常由socket函数得到,第二个参数由sockaddr_in结构体指针强制转化得到,第三个参数由sizeof sockaddr_in结构体变量得到,对于结构体sockaddr_in的初始化主要是sin_family,端口,IP地址三个成员的初始化。

3)bind函数:int bind(int sockfd, const struct sockaddr *myaddr,socklen_t addrlen);bind函数用在服务器,返回值0成功,-1失败。参数同connect函数。

4)listen函数:int listen(int sockfd, int backlog);sockfd是要监听到的端口,一直存在,backlog是监听套接口维护连接队列的最大值(三次握手完成队列,和三次握手未完成队列之和),为了方便期间我们通常设置backlog为5.

5)accept函数:int accept(int sockfd, sturct sockaddr *cliaddr,socklen_t * addrlen)如果调用成果返回一个已经连接的套接字,当连接结束后需要关闭此套接字,区别一直存在的监听套接字,同时accept函数还可以返回对端的sockaddr结构体,以及addrlen(——结果参数,因为传递的指针,所以核可以改变长度的值,再传给服务器,这样就可以得到此结构中确切存储了多少信息),所以说accept函数返回了三个值。

6)fork函数和exec函数用于nuix中派生新的进程。

7)close函数用于关闭套接字 int close(int sockfd)。注意调用close函数并不一定会在某个TCP连接上发送FIN终止序列,因为close函数只是将相应的描述字的引用计数减1,如果这记数值仍然大于0,那么close调用并不会引发TCP的四个分组连接中止序列。

8)标准输出函数:fgets函数和fputs函数char *fgets(char *s, int size, FILE *stream);

功能:从文件流读取一行,送到缓冲区,使用时注意以下几点:

当遇到换行符或者缓冲区已满,fgets就会停止,返回读到的数据,值得注意的是不能用fgets读二进制文件,因为fgets会把二进制文件当成文本文件来处理,这势必会产生乱码,比如二进制的0,那么fgets对0的解释是返回数据的结尾,所以发送的数据可能不包含末尾的换行符号,要是服务器对端等待发送的换行符而阻塞,可能导致死锁

每次调用,fgets都会把缓冲区的最后一个字符设为null,这意味着最后一个字符不能用来存放需要的数据,所以如果有一行,含有LINE_SIZE个字符(包括换行符),要想把这行读入缓冲区,请把参数n设为LINE_SIZE+1
int fputs(const char *s, FILE *stream);
int puts(const char *s);

返回值:成功返回一个非负整数,出错返回EOF

缓冲区s中保存的是以'\0'结尾的字符串,fputs将该字符串写入文件stream,但并不写入结尾的'\0'。与fgets不同的是,fputs并不关心的字符串中的'\n'字符,字符串中可以有'\n'也可以没有'\n'。puts将字符串s写到标准输出(不包括结尾的'\0'),然后自动写一个' \n'到标准输出。


示例:(比较前面的简单示例增加了处理僵死进程更加健壮)。参考unix网络编程第五章:简单的回射客户/服务器

服务器端:

#include<sys/stat.h>
#include<errno.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<signal.h>
#include<unistd.h>
#define SERVER_PORT 5555
#define MAXLINE 1024

void str_echo(int sockfd);
void sig_chld(int signo);
int main(int argc, char **argv){
int listenfd, connfd;
pid_t childpid;
int clilen;
struct sockaddr_in cliaddr, servaddr;
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
  perror("socket");
  exit(1);
}
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERVER_PORT);
if(bind(listenfd, (struct sockaddr*)&servaddr,sizeof(servaddr)) < 0)
{
perror("bind");
exit(1);
}
if(listen(listenfd, 5) < 0)
{
perror("listen");
exit(1);
}
signal(SIGCHLD, sig_chld);
for(; ;) {
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr*)&cliaddr,&clilen );
if(connfd < 0)
{
if(errno == EINTR)
continue;

else{
perror("accept");
exit(1);
}
}
if((childpid = fork()) == 0){
close(listenfd);
str_echo(connfd);
exit(0);
}
close(connfd);
}
return 0;
}
void str_echo(int sockfd)
{
int n,l;
char buf[MAXLINE];
again:
while((n = read(sockfd, buf, MAXLINE - 1)) > 0){
buf[n-1] = 'a';
buf[n] = '\n';
buf[n + 1] = '\0';
write(sockfd, buf, n + 1);
}
if(n < 0 && errno == EINTR)
goto again;
else {
if(n < 0)
{
perror("read");
exit(1);
}
}
return;
}
void sig_chld(int signo){
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
printf("child %d terminated\n", pid);
}
return;
}

客户端:

#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>

#define MAXLINE 1024

void str_cli(FILE* fp, int sockfd);
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if(argc != 2)
{
 perror("arguments");
exit(1);
}
if((sockfd = socket(AF_INET, SOCK_STREAM,0))  < 0)
{
perror("socket error");
exit(1);
}
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(5555);
servaddr.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sockfd,(struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect");
exit(1);
}
str_cli(stdin, sockfd);
exit(0);
}
void str_cli(FILE *fp, int sockfd)
{
char sendline[MAXLINE], recvline[MAXLINE];
while(fgets(sendline, MAXLINE, fp) != NULL)
{
printf("sendline is %d %d\n",strlen(sendline), sizeof(sendline));
write(sockfd, sendline,strlen(sendline));
if(read(sockfd, recvline, MAXLINE) < 0)
{
perror("read");
exit(1);
}
fputs(recvline, stdout);
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值