TCP/IP 网络编程

网络编程和套接字

TCP/IP协议族

应用层 HTTP FTP DNS NFS

传输层 TCP UDP

网络层 IP ARP RARP ICMP

数据链路层 Ethernet

UDP

格式:8B UDP报头,0~65527B 数据

头部格式:源端口,目标端口,长度,校验和(各2B)

面向无连接的不安全报式传输

不会握手,数据发出去就不管了

数据包丢失 就是全丢,不存在丢失一半

TCP

格式:头部(20~60B),数据(0-35535B)

头部格式:源端口,目标端口,序号,确认号,等等。。。。

面向连接的,流式传输协议,进行三次握手

数据发送 会数据确认,数据丢失会数据重传

TCPserver流程

socket()

bind()

listen()

accept()

hello_server.c

1

2

3

#include

int

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

#include

#include

#include

#include

#include

#include

void err_handling(char *message);

int main(int argc,char *argv[])

{

int serv_sock;

int clnt_sock;

struct sockaddr_in serv_addr;

struct sockaddr_in clnt_addr;

socklen_t clnt_addr_size;

char message[]="Hello world";

if(argc!= 2)

{

printf("Usage : %s \n ",argv[0]);

exit(0);

}

serv_sock = socket(PF_INET,SOCK_STREAM,0);

if(serv_sock == -1)

err_handling("socket() err");

memset(&serv_addr,0,sizeof(serv_addr));

serv_addr.sin_family=AF_INET;

serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);

serv_addr.sin_port=htons(atoi(argv[1]));

if(bind(serv_sock,(struct sockaddr*) &serv_addr,sizeof (serv_addr))==-1)

err_handling("bind() err");

if(listen(serv_sock,5) == -1)

err_handling("listen() err");

clnt_addr_size=sizeof (clnt_addr);

clnt_sock= accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_size);

if(clnt_sock==-1)

err_handling("accept() err");

write(clnt_sock,message,sizeof (message));

close(clnt_sock);

close(serv_sock);

return 0;

}

void err_handling(char *message)

{

fputs(message,stderr);

fputc('\n',stderr);

exit(1);

}

TCPserver流程

socket()

connect()

hello client.c

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

#include

#include

#include

#include

#include

#include

void err_handling(char *message);

int main(int argc,char *argv[])

{

int sock;

struct sockaddr_in serv_addr;

char message[30];

int strlen;

if(argc!=3)

{

printf("Usage : %s \n",argv[0]);

exit(1);

}

sock = socket(PF_INET,SOCK_STREAM,0);

if(sock == -1)

{

err_handling("socket() error");

}

memset(&serv_addr,0,sizeof (serv_addr));

serv_addr.sin_family=AF_INET;

serv_addr.sin_addr.s_addr=inet_addr(argv[1]);

serv_addr.sin_port=htons(atoi(argv[2]));

if(connect(sock,(struct sockaddr*)&serv_addr,sizeof (serv_addr))==-1)

err_handling("connect() err");

strlen = read(sock,message,sizeof(message)-1);

if(strlen==-1)

err_handling("read() err");

printf("Message froom server:%s \n",message);

close(sock);

return 0;

}

void err_handling(char *message)

{

fputs(message,stderr);

fputc('\n',stderr);

exit(1);

}

Windows实现

Windows下的项目配置

导入头文件 winsock2.h

链接ws2_32.lib库

属性-> 链接器 -> 输入 ->附加依赖项 -> ws2_32.lib

hello_server_win.c

1

//TODO

hello_client_win.c

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#include

#include

#include

#include

#define BUF_SIZE 1024

void ErrorHandling(const char *message);

int main(int argc, char *argv[])

{

WSADATA wsaData;

SOCKET hSocket;

char message[BUF_SIZE];

int strLen;

SOCKADDR_IN servAdr;

if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)

ErrorHandling("WSAStartup() error!");

hSocket = socket(PF_INET, SOCK_STREAM, 0);

if (hSocket == INVALID_SOCKET)

ErrorHandling("socket() error");

memset(&servAdr, 0, sizeof(servAdr));

servAdr.sin_family = AF_INET;

servAdr.sin_addr.s_addr = inet_addr("118.24.137.128");

servAdr.sin_port = htons(atoi("16666"));

if (connect(hSocket, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)

ErrorHandling("connect() error!");

else

puts("Connected...........");

while (1)

{

fputs("Input message(Q to quit): ", stdout);

fgets(message, BUF_SIZE, stdin);

if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))

break;

send(hSocket, message, strlen(message), 0);

strLen = recv(hSocket, message, BUF_SIZE - 1, 0);

message[strLen] = 0;

printf("Message from server: %s", message);

}

closesocket(hSocket);

WSACleanup();

return 0;

}

void ErrorHandling(const char *message)

{

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

套接字类型和协议设置

协议族 Protocol Family

PF_INET IPV4互联网协议族

PF_INET6 IPV6互联网协议族

PF_LOCAL 本地通信UNIX协议族

1

2

3

4

5

//TCP

int tcp_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

//UDP

int udp_socket = socket(PF_INET,SOCK_DGRAM.IPPROTO_UDP)

地址族和数据序列

表示ipv4地址的结构体

1

2

3

4

5

6

7

8

9

10

11

12

struct sockaddr_in

{

sa_family_t sin_family; //地址族

uint16_t sin_port; //16位TCP/UDP端口号

struct in_addr sin_addr; //32位ip地址

char sin_zero[8];//不使用

};

struct in_addr

{

In_addr_t s_addr; //32位ipv4地址

};

网络字节序与地址变换

约定:统一大端序

大端(Big Endian) 高位字节存放到地位地址(像字符串)

小端(Little Endian) 高位字节存放到高位地址

0x12345678

大端:12 34 56 78

小端:78 56 34 12

字节序转换

1

2

3

4

unsigned short htons(unsigned short);

unsigned short ntohs(unsigned short);

unsigned long htonl(unsigned long);

unsigned long ntohl(unsigned long);

h代表主机host n表示网络network s指short l指long

(Intel AMD常用小端序 ARM常用大端序)

ip地址的转换

1

2

3

4

5

6

7

8

9

10

11

#include

in_addr_t inet_addr(const char* string);

//成功时返回32位大端序整数值ip 失败返回INADDR_NONE

int inet_aton(const char *string struct in_addr *addr);

// string :含有ip的地址字符串

//adddr 将保存转换结果的in_addr结构体的地址

//成功返回1 失败返回0 更常用

char * inet_ntoa(struct in_addr adr)

//成功返回字符串地址 失败返回-1

网络地址的初始化

1

2

3

4

5

6

7

struct sockaddr_in addr;

char * serv_ip = "118.24.137.128";//ip

char * serv_port = "9090";//端口字符串

memset(&addr,0,sizeof (serv_addr));//所有成员初始化为0

addr.sin_family=AF_INET;//指定地址族

addr.sin_addr.s_addr=inet_addr(serv_ip);//基于字符串的IP址初始化

addr.sin_port=htons(atoi(serv_port));//基于字符串的端口号始化

INADDR_ANY

1

2

3

4

5

6

struct sockaddr_in addr;

char * serv_port = "9090";

memset(&addr,0,sizeof (serv_addr));

addr.sin_family=AF_INET;

addr.sin_addr.s_addr=htonl(INADDR_ANY);//☆☆☆☆

addr.sin_port=htons(atoi(serv_port));

服务器常用

TCP服务器与客户端

TCP服务端在accept后返回客户端的socket

客户端在connect后分配随机的端口

TODO echo的例子

UDP的服务端与客户端

UDP的客户端和服务器均只需要一个套接字

基于UDP的数据IO函数

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include

ssize_t sendto(

int sock, //用于传输数据的UDP套接字描述符

void *buff, //待传输数据的地址

size_t nbytes,//待传输数据的长度 字节为单位

int flags,//可选参数 没有传递0

struct sockaddr *to,//目标地址 sockaddr结构体 的地址

socklen_t addrlen//结构体变量长度

);

//成功返回传输的字节数 失败返回-1

ssize_t recvfrom(

int socket, //用于接收数据的UDP套接字描述符

void *buff, //保存接收数据缓冲的地址

size_t nbytes, //可接收的最大字节数

int flags, //可选参数 没有传入0

struct sockaddr *from, //存有发送端地址的的sockaddr的地址

socklen_t *addrlen // 保存参数from结构体变量长度的地址

);

//成功返回接收的字节数 失败返回-1

UDP echo

server

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

#include

#include

#include

#include

#include

#include

#define BUF_SIZE 30

void error_handling(char *message);

int main(int argc, char *argv[])

{

int serv_sock;

char message[BUF_SIZE];

int str_len;

socklen_t clnt_adr_sz;

struct sockaddr_in serv_adr, clnt_adr;

if (argc != 2)

{

printf("Usage: %s \n", argv[0]);

exit(1);

}

serv_sock = socket(PF_INET, SOCK_DGRAM, 0);

if (serv_sock == -1)

{

error_handling("UDP socket creating error");

}

memset(&serv_adr, 0, sizeof(serv_adr));

serv_adr.sin_family = AF_INET;

serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);

serv_adr.sin_port = htons(atoi(argv[1]));

if (bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1)

{

error_handling("bind err");

}

while(1)

{

clnt_adr_sz = sizeof(clnt_adr);

str_len=recvfrom(serv_sock,message,BUF_SIZE,0,(struct sockaddr*)&clnt_adr,&clnt_adr_sz);

message[str_len]=0;

printf("Message from client: %s",message);

sendto(serv_sock,message,str_len,0,(struct sockaddr*)&clnt_adr,clnt_adr_sz);

}

close(serv_sock);

return 0;

}

void error_handling(char *message)

{

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

client

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

#include

#include

#include

#include

#include

#include

#define BUF_SIZE 30

void error_handling(char *message);

int main(int argc, char *argv[])

{

int sock;

char message[BUF_SIZE];

int str_len;

socklen_t adr_sz;

struct sockaddr_in serv_adr, from_adr;

if (argc != 3)

{

printf("Usage: %s \n", argv[0]);

exit(1);

}

sock = socket(PF_INET, SOCK_DGRAM, 0);

if (sock == -1)

{

error_handling("socket() error");

}

memset(&serv_adr, 0, sizeof(serv_adr));

serv_adr.sin_family = AF_INET;

serv_adr.sin_addr.s_addr = inet_addr(argv[1]);

serv_adr.sin_port = htons(atoi(argv[2]));

while(1)

{

fputs("Insert message(q to quit):",stdout);

fgets(message, sizeof(message), stdin);

if (!strcmp(message, "q\n"))

{

break;

}

sendto(sock,message,strlen(message),0,(struct sockaddr*)&serv_adr, sizeof(serv_adr));

adr_sz = sizeof(from_adr);

str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz);

message[str_len]=0;

printf("Message from server %s",message);

}

close(sock);

return 0;

}

void error_handling(char *message)

{

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

UDP数据报存在边界

Python 取本机MAC地址

1

2

3

4

5

6

import uuid

node = uuid.uuid1()

print('node=',node)

hex=node.hex

mac_addr=hex[-12:]

print('mac_addr=',mac_addr)

友情链接:
Copyright © 2022 86年世界杯_世界杯预选赛阿根廷 - fjyfzz.com All Rights Reserved.