avatar

目录
学习计算机网络笔记(Socket篇)

非常惭愧,学习计算机这么久,一直没有系统的学习过计算机网络,现在因为插本需要,所以必须要学习计算机网络,在网易云课堂中,找到了一个大学生课堂的视频链接,其中有很多高等学院的视频教程。

顶尖中文大学计算机专业课程体系

因为是平时看视频教程都习惯开1.5倍 - 2倍速, 害怕忘记,所以记个笔记。

Socket编程API函数

Socket

c
1
2
3
4
5
6
7
8
9
10
sd = socket(protofamily,type,proto);
创建套接字

操作系统返回套接字描述(sd)

第一个参数(协议族):protofamily = PF_INET(TCP/IP)

第二个参数(套接字类型):type = SOCK_STREAM,SOCK_DGRAM OR SOCK_RAW(TCP/IP)

第三个参数(协议号):0为默认

SOCK_STREAM 走 TCP , SOCK_DGRAM 走 UDP, SOCK_RAW 跳过传输层,直接到网络层

bind

服务器端连接,有多个IP地址访问时,可以使用地址通配符:INADDR_ANY

Connect

客户端 建立TCP连接返回成功,证明与服务器连接成功

客户端建立UDP连接返回成功,不一定与服务器连接上,只是指定了远端的服务器地址

Accept

多线程 并发 时候,使用accept 连接。

服务器端不能使用Connect()函数

无连接服务器使用sendto()函数发送数据报

服务器端连接流程

循环无连接服务器

循环面向连接服务器

并发无连接服务器

并发面向连接服务器

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//consock.cpp
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<winsock.h>

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif /* INADDR_NONE*/

void errexit(const char*,...);
/*
connectsock - allocate & connect a socket using tcp or udp
connectsock - 使用tcp或udp分配和连接套接字
*/

SOCKET connectsock(const char *host, const char *service, const char *transport)
{
/*
struct hostent
{
char *h_name; //正式主机名
char **h_aliases; //主机别名
int h_addrtype; //主机IP地址类型:IPV4-AF_INET
int h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位
char **h_addr_list; //主机的IP地址列表
};

struct servent
{
char FAR* s_name; //官方服务名称
char FAR* FAR* s_aliases; //其他别名
short s_port; //此服务的端口
char FAR* s_proto; //使用协议
};

struct protoent
{
char FAR* p_name; //正式协议名称
char FAR* FAR* p_aliases; //允许的别名列表
short p_proto; //正式协议号
};

struct sockaddr_in {
short sin_family; //地址族(TCP/IP:AF_INET)
u_short sin_port; //端口号
struct in_addr sin_addr; //IP地址
char sin_zero[8]; //未用(置0)
};
*/
struct hostent *phe; /* hostent是C语言标准库函数gethostbyname的返回值 */
struct servent *pse; /* 需要将服务名转为熟知端口号 getservbyname*/
struct protoent *ppe; /* 实现协议名到协议号的转换getprotobyname */
struct sockaddr_in sin; /* 使用TCP/IP 协议 的网络应用程序声明端点地址变量时,使用sockaddr_in*/
int s, type;

memset(&sin, 0, sizeof(sin)); // void *memset(void *s, char ch, unsigned n); 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值
sin.sin_family = AF_INET;
/* Map service name to port number
将服务名称映射到端口号
*/
if (pse = getservbyname(service, transport))
sin.sin_port = pse ->s_port; /*“->”是一个整体,它是用于指向结构体子数据的指针,用来取子数据。 pse 所指向的 s_port的值 赋值给 sin.sin_port*/
else if ((sin.sin_port = htons((u_short)atoi(service))) == 0)
errexit("can't get \"%s\"service entry\n", service);
/* Map host name to IP address, allowing for dotted decimal
将主机名映射到IP地址,允许使用点分十进制
*/
if (phe = gethostbyname(host))
/* memcpy 内存复制
void *memcpy(void *dest, const void *src, size_t n); 它的功能是从src的开始位置拷贝n个字节的数据到dest。如果dest存在数据,将会被覆盖。memcpy函数的返回值是dest的指针。
*/
memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
errexit("can't get\"%s\"host entry\n", host);
/* Map protocol name to protocol number
将协议名称映射到协议编号
*/
if ((ppe = getprotobyname(transport)) == 0)
errexit("can't get \"%s\" protocol entry\n", transport);
/* Use protocol to choose a socket type
使用协议选择套接字类型
*/
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
/* Allocate a socket
分配套接字
*/
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket:%d\n", GetLastError());
/* Connect the socket
连接套接字
*/
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
errexit("can't connect to %s.%s:%d \n", host, service, GetLastError());
return s;
}
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//conUDP.cpp
#include<winsock.h>

SOCKET connectsock(const char *, const char *, const char *);
/*
*------------------------------------------------------------
* connectUDP - connect to a specified UDP service 连接到指定的UDP服务
* on a specified host 在指定的主机上
*------------------------------------------------------------
*/

SOCKET connectUDP(const char *host, const char *service)
{
return connectsock(host, service, "udp");
}
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//conTCP.cpp
#include<winsock.h>

SOCKET connectsock(const char *, const char *, const char *);
/*
*------------------------------------------------------------
* connectTCP - connect to a specified TCP service 连接到指定的TCP服务
* on a specified host 在指定的主机上
*------------------------------------------------------------
*/
SOCKET connectTCP(const char *host, const char *service)
{
return connectsock(host, service, "tcp");
}
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
//errexit.cpp
#include<stdarg.h>
#include<stdio.h>
#include<stdlib.h>
#include<winsock.h>
/*
*------------------------------------------------------------
errexit - print an error message and exit
打印错误信息并退出
*------------------------------------------------------------
*/
/* VARARGS1 */
void errexit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
WSACleanup();
exit(1);
}
/*
不定型函数定义,"..."代表可变参数,且省略号必须位于参数表的尾部
void errexit(const char *format,...)
定义不变函数,需要引入标准头文件<stdarg.h>,使用库中一些宏实现可变参数的遍历
需要先定义一个指针遍历参数表 va_list args;
接着,需要用 va_start 对args 进行初始化 va_start(args,format); 使他指向第一个无名参数(事实上更准确的描述应该是:以最后一个有名参数为起点,因为事实上是可以不传入无名参的。)
当遍历完成,需要使用 va_end 完成清理工作 va_end(args);

*/
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
60
61
62
63
64
65
//TCPdtc.cpp
#include<stdlib.h>
#include<stdio.h>
#include<winsock.h>

void TCPdaytime(const char *, const char *);
void errexit(const char *, ...);
SOCKET connectTCP(const char *, const char *);

#define LINELEN 128
#define WSVERS MAKEWORD(2,0)
/*
*------------------------------------------------------------
* main - TCP client for DAYTIME service
* main - 用于DAYTIME 服务的TCP客户端
*------------------------------------------------------------
*/
int main(int argc, char *argv[])
{
char *host = (char *)"localhost";
char *service = (char *)"daytime";
WSADATA wsadata;
switch (argc) {
case 1:
host = (char *)"localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage:TCPdaytime [host[port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
TCPdaytime(host, service);
WSACleanup();
getchar();
return 0;
}
/*
*----------------------------------------------------------------------
* TCPdaytime - invoke Daytime on specified host and print results
* TCPdaytime - 在指定主机上调用Daytime并打印结果
*----------------------------------------------------------------------
*/
void TCPdaytime(const char *host, const char *service)
{
char buf[LINELEN + 1];
SOCKET s;
int cc;
s = connectTCP(host, service);

cc = recv(s, buf, LINELEN, 0);
while (cc != SOCKET_ERROR && cc > 0)
{
buf[cc] = '\0';
(void)fputs(buf, stdout);
cc = recv(s, buf, LINELEN, 0);
}
closesocket(s);
}
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
60
61
62
63
64
65
66
67
68
69
70
71
//UDPdtc.cpp
#include<stdlib.h>
#include<stdio.h>
#include<winsock.h>

void UDPdaytime(const char *, const char *);
void errexit(const char *, ...);
SOCKET connectUDP(const char *, const char *);

#define LINELEN 128
#define WSVERS MAKEWORD(2,0)
#define MSG "what daytime is it\n"
/*
*------------------------------------------------------------
* main - UDP client for DAYTIME service
* main - 用于DAYTIME 服务的UDP客户端
*------------------------------------------------------------
*/

int main(int argc, char *argv[])
{
char *host = (char *)"localhost";
char *service = (char *)"daytime";
WSADATA wsadata;
switch (argc) {
case 1:
host = (char *)"localhost";
break;
case 3:
service = argv[2];
/* FALL THROUGH */
case 2:
host = argv[1];
break;
default:
fprintf(stderr, "usage:UDPdaytime [host[port]]\n");
exit(1);
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
UDPdaytime(host, service);
WSACleanup();
return 0;
}
/*
*----------------------------------------------------------------------
* UDPdaytime - invoke Daytime on specified host and print results
* UDPdaytime - 在指定主机上调用Daytime并打印结果
*----------------------------------------------------------------------
*/
void UDPdaytime(const char *host, const char *service)
{
char buf[LINELEN + 1];
SOCKET s;
int n;
int cc;

s = connectUDP(host, service);
(void)send(s, MSG, strlen(MSG), 0);
/* UDP 发送数据报,完整的信息,不能分块*/
n = recv(s, buf, LINELEN, 0);
if (n == SOCKET_ERROR)
errexit("recv failed:recv() error %d\n", GetLastError());
else
{
buf[cc] = '\0';
(void)fputs(buf, stdout);

}
closesocket(s);
}
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
//passivesock.cpp
#include<stdlib.h>
#include<string.h>
#include<winsock.h>

void errexit(const char *, ...);

/*
*----------------------------------------------------------------------
* passivesock - allocate & bind a server socket using TCP or UDP
*----------------------------------------------------------------------
*/
SOCKET passivesock(const char *service, const char *transport, int qlen)
{
struct servent *pse;
struct protoent *ppe;
struct sockaddr_in sin;
SOCKET s;

int type;

memset(&sin, 0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;

if (pse = getservbyname(service, transport))
sin.sin_port = (u_short)pse->s_port;
else if ((sin.sin_port = htons((u_short)atoi(service))) == 0)
errexit("can't get \"%s\" service entry\n", service);

if ((ppe = getprotobyname(transport)) == 0)
errexit("can't get\"%s\" protocol entry\n", transport);
if (strcmp(transport, "udp") == 0)
type = SOCK_DGRAM;
else
type = SOCK_STREAM;
s = socket(PF_INET, type, ppe->p_proto);
if (s == INVALID_SOCKET)
errexit("can't create socket:%d\n", GetLastError());
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
errexit("can't bind to %s port :%d\n", service);
if (type == SOCK_STREAM && listen(s, qlen) == SOCKET_ERROR)
errexit("can't listen on %s port:%d\n", service, GetLastError());
return s;
}
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//passTCP.cpp
#include<winsock.h>

SOCKET passivesock(const char *, const char *, int);
/*
*----------------------------------------------------------------------
* passiveTCP - create a passive socket for use in a TCP server
*----------------------------------------------------------------------
*/

SOCKET passiveTCP(const char *service, int qlen)
{
return passivesock(service, "tcp", qlen);
}
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
//UDPdtd.cpp
#include<stdlib.h>
#include<winsock.h>
#include<time.h>

void errexit(const char *, ...);
SOCKET passiveUDP(const char *);
#define WSVERS MAKEWORD (2,0)

/*
*----------------------------------------------------------------------
* main - lterative UDP server for DAYTIME service
*----------------------------------------------------------------------
*/
void main(int argc, char *argv[])
{
struct sockaddr_in fsin;
char * service = (char *)"daytime";
SOCKET sock;
int alen;
char * pts;
time_t now;
WSADATA wsadata;
char *buf;

switch (argc)
{
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage:UDPdaytimed[port]\n");
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStartup failed\n");
sock = passiveUDP(service);
while (1)
{
alen = sizeof(struct sockaddr);
if (recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&fsin, &alen) == SOCKET_ERROR)
errexit("recvfrom:error %d\n", GetLastError());
(void)time(&now);
pts = ctime(&now);
(void)sendto(sock, pts, strlen(pts), 0, (struct sockaddr *)&fsin, sizeof(fsin));

}

}
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
60
61
62
63
64
#include<stdlib.h>
#include<winsock.h>
#include<process.h>
//TCPdtd.cpp
#include<time.h>

void errexit(const char *, ...);
void TCPdaytimed(SOCKET);
SOCKET passiveTCP(const char *, int);

#define QLEN 5
#define WSVERS MAKEWORD(2,0)
/*
*----------------------------------------------------------------------
* main - lterative TCP server for DAYTIME service
*----------------------------------------------------------------------
*/
void main(int argc, char *argv[])
{
struct sockaddr_in fsin;
char *service = (char *)"daytime";
SOCKET msock, ssock;
int alen;
WSADATA wsadata;

switch (argc)
{
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage:TCOdaytimed [port]\n");
}
if (WSAStartup(WSVERS, &wsadata) != 0)
errexit("WSAStarup failed\n");
msock = passiveTCP(service, QLEN);
while (1)
{
alen = sizeof(struct sockaddr);
ssock = accept(msock, (struct sockaddr *)&fsin, &alen);
if (ssock == INVALID_SOCKET)
errexit("accept failed:error number %d\n", GetLastError());
if (_beginthread((void(*)(void *))TCPdaytimed, 0, (void *)ssock) < 0)
{
errexit("_beginthread:%s\n",strerror(errno));
}
}
}
/*
*----------------------------------------------------------------------
* TCPdaytimed - do TCP DAYTIME protocol
*----------------------------------------------------------------------
*/
void TCPdaytimed(SOCKET fd)
{
char * pts;
time_t now;
(void)time(&now);
pts = ctime(&now);
(void)send(fd, pts, strlen(pts), 0);
(void)closesocket(fd);
}
c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//passUDP.cpp
#include<winsock.h>

SOCKET passivesock(const char *, const char *, int);

/*
*----------------------------------------------------------------------
* passiveUDP - create a passive socket for use in a UDP server
*----------------------------------------------------------------------
*/

SOCKET passiveUDP(const char *service)
{
return passivesock(service, "udp", 0);
}
文章作者: KeyboArd
文章链接: https://www.wrpzkb.cn/socket/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 KeyboArd's Blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论