Linux网络编程<三>

微信公众号文章链接:https://mp.weixin.qq.com/s?__biz=Mzg5MjAxNzEyMg==&mid=2247484339&idx=1&sn=9789bc9e98852ec0f89c53c16bf2bc5e&chksm=cfc5c33ef8b24a2858ef5852dba89c5cf8ce77aaaed86e91b94bca9018ed3ca41b3ae668ab39&token=418739389&lang=zh_CN#rd

前2篇文章,我写了一些网络编程的基础以及一些网络编程需要掌握的基础。

1:Linux网络编程<一>

2:Linux网络编程<二>

socket编程

TCP协议的流程图

收到确认消息后才会继续发送消息,否则继续等待。这样的好处是传输的数据是可靠的,此外它是有连接的传输,大多数网络传输都是用的TCP。

TCP协议

(1)面向连接的可靠的传输控制协议,连接的建立需要三次握手,连接的     释放需要进行四次握手才能保证连接的建立,数据的同步传输。

(2)面向字节流,会把从上层传输下来的数据当作是无结构的字节流。

(3)一对一的通信。

(4)TCP在IP协议的基础之上添加了序号机制,确认机制,超时重传机制,数据校验,从而保证传输的可靠性,同时保证不出现丢失或者是乱序。

TCP通信的基本步骤如下:

服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close

客户端:socket---------connect---send---recv-----------------close

服务器端:

1. 创建socket

1sock_fd = socket(AF_INET, SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP

2if(-1== sock_fd)

3{

4fprintf(stderr,"socket error:%s\n\a", strerror(errno));

5exit(1);

6}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int socket(int domain, int type, int protocol);

函数功能

创建一个套接字;

domain:协议域(族),决定了套接字的地址类型,例如AF_INET决定了要用IPv4地址(32位)与端口号(16位)的组合。常见的协议族有:AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX)、AF_ROUTE等;

type:指定套接字类型SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW

protocol:指定socket所使用的传输协议编号,通常为0

返回值

若成功,返回一个套接字描述符,否则返回-1;

Socket就是一种文件描述符,和普通的打开文件一样,需要检测其返回结果。

2. 设置socket

1memset(&server_addr,0,sizeof(struct sockaddr_in));//clear

2server_addr.sin_family = AF_INET;

3server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY:This machine all IP

4server_addr.sin_port = htons(PORT_NUMBER);

设置何种协议族,设置本机IP和端口,也就有了唯一性。

3. 绑定socket

1ret = bind(sock_fd, (struct sockaddr *)(&server_addr),sizeof(struct sockaddr));

2if(-1== ret)

3{

4fprintf(stderr,"bind error:%s\n\a", strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int bind(int sockfd, struct sockaddr *addr, int addrlen);

函数功能

把套接字绑定到本地计算机的某一个端口上;

sockfd:待绑定的套接字描述符

addr:一个struct sockaddr *指针,指定要绑定给sockfd的协议地址。内容结构由前面的协议族决定。

addrlen:地址的长度

返回值

若成功,返回0,否则返回-1,错误信息存在errno中;

4. 开始监听

1ret =listen(sock_fd, BACKLOG);

2if(-1== ret)

3{

4fprintf(stderr,"listen error:%s\n\a", strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int listen(int sockfd, int backlog);

函数功能

使服务器的这个端口和IP处于监听状态,等待网络中某一客户机的连接请求,最大连接数量为backlog≤128;

sockfd:待监听的套接字描述符

backlog:最大可监听和连接的客户端数量

返回值

若成功,返回0,否则返回-1;

5. 阻塞,等待连接

1addr_len =sizeof(struct sockaddr);

2new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &addr_len);

3if(-1== new_fd)

4{

5fprintf(stderr,"accept error:%s\n\a", strerror(errno));

6close(sock_fd);

7exit(1);

8}

1

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int accept(int sockfd, struct sockaddr *addr, int *addrlen);

函数功能

接受连接请求,建立起与客户机之间的通信连接。服务器处于监听状态时,如果某时刻获得客户机的连接请求,此时并不是立即处理这个请求,而是将这个请求放在等待队列中,当系统空闲时再处理客户机的连接请求;

当accept函数接受一个连接时,会返回一个新的socket标识符,以后的数据传输和读取就要通过这个新的socket编号来处理,原来参数中的socket也可以继续使用,继续监听其它客户机的连接请求;

accept连接成功时,参数addr所指的结构体会填入所连接机器的地址数据;

sockfd:待监听的套接字描述符

addr:指向struct sockaddr的指针,用于返回客户端的协议地址

addrlen:协议地址的长度

返回值

若成功,返回一个由内核自动生成的一个全新描述字,代表与返回客户的TCP连接,否则返回-1,错误信息存在errno中;

6. 接收数据

1recv_len =recv(new_fd, recv_buf,999,0);

2if(recv_len <=0)

3{

4fprintf(stderr,"recv error:%s\n\a", strerror(errno));

5close(new_fd);

6exit(1);

7}

8else

9{

10recv_buf[recv_len] ='\0';

11printf("Get msg from client%d: %s\n", client_num, recv_buf);

12}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int recv(int sockfd, void *buf, size_t len, int flags);

函数功能

用新的套接字来接收远端主机传来的数据,并把数据存到由参数buf指向的内存空间;

sockfd:sockfd为前面accept的返回值,即new_fd,也就是新的套接字

buf:指明一个缓冲区

len:指明缓冲区的长度

flags:通常为0

返回值

若成功,返回接收到的字节数,另一端已关闭则返回0,否则返回-1,错误信息存在errno中;

7. 关闭socket

1close(sock_fd);

2exit(0);

为了应对多个连接,并保证它们之间相互独立,实际编程中往往还要加入多进程fork()。

让子进程接收数据,父进程继续监听新的连接。

客户机端:

1. 创建socket

1sock_fd = socket(AF_INET, SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP

2if(-1== sock_fd)

3{

4fprintf(stderr,"socket error:%s\n\a", strerror(errno));

5exit(1);

6}

2. 设置socket

1memset(&server_addr,0,sizeof(struct sockaddr_in));//clear

2server_addr.sin_family = AF_INET;

3server_addr.sin_port = htons(PORT_NUMBER);

其中注意的是,这里设置的socket内容是指 希望连接的服务器IP和端口号信息,IP地址来自用户的输入,并转换格式得到。因此,这里的设置和服务器的设置,要保持内容上的一致。

1ret = inet_aton(argv[1], &server_addr.sin_addr);

2if(0== ret)

3{

4fprintf(stderr,"server_ip error.\n");

5close(sock_fd);

6exit(1);

7}

3. 连接

1ret = connect(sock_fd, (conststruct sockaddr *)&server_addr,sizeof(struct sockaddr));

2if(-1== ret)

3{

4fprintf(stderr,"connect error:%s\n\a", strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);

函数功能

用来请求连接远程服务器,将参数sockfd的socket连至参数serv_addr所指定的服务器IP和端口号上去;

sockfd:客户端的socket套接字

serv_addr:一个struct sockaddr类型的结构体指针变量,存储着远程服务器的IP与端口号信息

addrlen:结构体变量的长度

返回值

若成功,返回0,否则返回-1,错误信息存在errno中;

4. 发送

1send_buf = send(sock_fd, send_buf,strlen(send_buf),0);

2if(send_buf <=0)

3{

4fprintf(stderr,"send error:%s\n\a", strerror(errno));

5close(sock_fd);

6exit(1);

7}

所需要头文件

#include <sys/types.h>

#include <sys/socket.h>

函数格式

int send(int sockfd, const void *buf, int len, int flags);

函数功能

用来发送数据给指定的远端主机;

sockfd:客户端的socket套接字

buf:指明一个缓冲区

len:指明缓冲区的长度

flags:通常为0

返回值

若成功,返回发送的字节数,否则返回-1,错误信息存在errno中

5. 关闭socket

1close(sock_fd);

2exit(0);

TCP完整例子:

service:

1#include<stdio.h>

2#include<unistd.h>

3#include<sys/types.h>

4#include<sys/socket.h>

5#include<strings.h>

6#include<string.h>

7#include<ctype.h>

8#include<arpa/inet.h>

9

10#defineSERV_PORT 9527

11

12intmain(void)

13

{

14intsfd, cfd;

15intlen, i;

16charbuf[BUFSIZ], clie_IP[BUFSIZ];

17

18structsockaddr_inserv_addr,clie_addr;

19socklen_tclie_addr_len;

20

21/*创建一个socket 指定IPv4协议族 TCP协议*/

22sfd = socket(AF_INET, SOCK_STREAM,0);

23

24/*初始化一个地址结构 man 7 ip 查看对应信息*/

25bzero(&serv_addr,sizeof(serv_addr));//将整个结构体清零

26serv_addr.sin_family = AF_INET;//选择协议族为IPv4

27serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址

28serv_addr.sin_port = htons(SERV_PORT);//绑定端口号    

29

30/*绑定服务器地址结构*/

31bind(sfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr));

32

33/*设定链接上限,注意此处不阻塞*/

34listen(sfd,64);//同一时刻允许向服务器发起链接请求的数量

35

36printf("wait for client connect ...\n");

37

38/*获取客户端地址结构大小*/

39clie_addr_len =sizeof(clie_addr_len);

40/*参数1是sfd; 参2传出参数, 参3传入传入参数, 全部是client端的参数*/

41cfd = accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len);/*监听客户端链接, 会阻塞*/

42

43printf("client IP:%s\tport:%d\n",

44inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP,sizeof(clie_IP)),

45ntohs(clie_addr.sin_port));

46

47while(1) {

48/*读取客户端发送数据*/

49len = read(cfd, buf,sizeof(buf));

50write(STDOUT_FILENO, buf, len);

51

52/*处理客户端数据*/

53for(i =0; i < len; i++)

54buf[i] =toupper(buf[i]);

55

56/*处理完数据回写给客户端*/

57write(cfd, buf, len);

58}

59

60/*关闭链接*/

61close(sfd);

62close(cfd);

63

64return0;

65}

client:

1#include<stdio.h>

2#include<unistd.h>

3#include<string.h>

4#include<sys/socket.h>

5#include<arpa/inet.h>

6

7#defineSERV_IP"127.0.0.1"

8#defineSERV_PORT 9527

9

10intmain(void)

11

{

12intsfd, len;

13structsockaddr_inserv_addr;

14charbuf[BUFSIZ];

15

16/*创建一个socket 指定IPv4 TCP*/

17sfd = socket(AF_INET, SOCK_STREAM,0);

18

19/*初始化一个地址结构:*/

20bzero(&serv_addr,sizeof(serv_addr));//清零

21serv_addr.sin_family = AF_INET;//IPv4协议族

22inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr);//指定IP 字符串类型转换为网络字节序 参3:传出参数

23serv_addr.sin_port = htons(SERV_PORT);//指定端口 本地转网络字节序

24

25/*根据地址结构链接指定服务器进程*/

26connect(sfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr));

27

28while(1) {

29/*从标准输入获取数据*/

30fgets(buf,sizeof(buf),stdin);

31/*将数据写给服务器*/

32write(sfd, buf,strlen(buf));//写个服务器

33/*从服务器读回转换后数据*/

34len = read(sfd, buf,sizeof(buf));

35/*写至标准输出*/

36write(STDOUT_FILENO, buf, len);

37}

38

39/*关闭链接*/

40close(sfd);

41

42return0;

43}

推荐阅读:

网络编程<一>

Linux I/O复用--epoll

Linux I/O复用——poll()

Linux I/O复用—select()

线程池网络服务

多线程网络服务

Socket网络编程

线程高级操作

Linux多线程编程

线程

进程间通信(IPC)

进程间通信(一)

进程间通信(二)

    觉得有用,点赞,分享就是对小编最大的支持

长按2秒识别二维码关注公众号

欢迎把我推荐给你的朋哟

每天进步一点点,如果有用给点个赞

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 系统与网络编程 select函数 select和pselect多用于I/O操作,他们见识多个文件描述符的集合,判断...
    I踏雪寻梅阅读 621评论 0 1
  • 下面为Daytime这个服务的源代码例子,同时兼容IPV6和IPV4的地址,最后部分有更多说明。 单播模式下的Se...
    天楚锐齿阅读 5,747评论 0 2
  • 网络编程基础网络编程,首先了解计算机网络体系结构是有必要的,着重掌握TCP、IP协议,理解socket的概念,理解...
    zhile_doing阅读 1,871评论 0 1
  • 大纲 一.Socket简介 二.BSD Socket编程准备 1.地址 2.端口 3.网络字节序 4.半相关与全相...
    VD2012阅读 2,436评论 0 5
  • 1、基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和po...
    Daniel521阅读 697评论 0 1