Mysql有两种连接方式:
(1),TCP/IP
(2),socket
对mysql.sock来说,其作用是程序与mysqlserver处于同一台机器,发起本地连接时可用。 (如果程序跟mysql在同一台机,可以使用mysql.sock通信)
例如你无须定义连接host的具体IP得,只要为空或localhost就可以。
在此种情况下,即使你改变mysql的外部port也是一样可能正常连接。
因为你在my.ini中或my.cnf中改变端口后,mysql.sock是随每一次 mysql server启动生成的。已经根据你在更改完my.cnf后重启mysql时重新生成了一次,信息已跟着变更。
那么对于外部连接,必须是要变更port才能连接的。
为什么mysql.sock可以跟php通信呢?进程之间的通信不是共享内存,管道,有名管道之类的吗?
下面是摘自维基百科的解释,(mysql.sock是Unix domain socket,这里的sockt并非是网络编程的socket哦)
Unix domain socket 或者 IPC socket是一种终端,可以使同一台操作系统上的两个或多个进程进行数据通信。与管道相比,Unix domain sockets 既可以使用字节流,又可以使用数据队列,而管道通信则只能使用字节流。Unix domain sockets的接口和Internet socket很像,但它不使用网络底层协议来通信。Unix domain socket 的功能是POSIX操作系统里的一种组件。
Unix domain sockets 使用系统文件的地址来作为自己的身份。它可以被系统进程引用。所以两个进程可以同时打开一个Unix domain sockets来进行通信。不过这种通信方式是发生在系统内核里而不会在网络里传播。
下面是对Unix domain socket 的详细解释
一、 概述
UNIX Domain Socket是在socket架构上发展起来的用于同一台主机的进程间通讯(IPC),它不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX Domain Socket有SOCK_DGRAM或SOCK_STREAM两种工作模式,类似于UDP和TCP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
UNIX Domain Socket可用于两个没有亲缘关系的进程,是全双工的,是目前使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIX Domain Socket通讯的。
二、工作流程
UNIX Domain socket与网络socket类似,可以与网络socket对比应用。
上述二者编程的不同如下:
- address family为AF_UNIX
- 因为应用于IPC,所以UNIXDomain socket不需要IP和端口,取而代之的是文件路径来表示“网络地址”。这点体现在下面两个方面。
- 地址格式不同,UNIXDomain socket用结构体sockaddr_un表示,是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
- UNIX Domain Socket客户端一般要显式调用bind函数,而不象网络socket一样依赖系统自动分配的地址。客户端bind的socket文件名可以包含客户端的pid,这样服务器就可以区分不同的客户端。
UNIX Domain socket的工作流程简述如下(与网络socket相同)。
服务器端:创建socket—绑定文件(端口)—监听—接受客户端连接—接收/发送数据—…—关闭
客户端:创建socket—绑定文件(端口)—连接—发送/接收数据—…—关闭
三、阻塞和非阻塞(SOCK_STREAM方式)
读写操作有两种操作方式:阻塞和非阻塞。
1.阻塞模式下
阻塞模式下,发送数据方和接收数据方的表现情况如同命名管道,参见本人文章“Linux下的IPC-命名管道的使用()”
2.非阻塞模式
在send或recv函数的标志参数中设置MSG_DONTWAIT,则发送和接收都会返回。如果没有成功,则返回值为-1,errno为EAGAIN 或 EWOULDBLOCK。
四、测试代码
服务器端
[cpp] view plain copy
1. #include <stdio.h>
2. #include <sys/stat.h>
3. #include <sys/socket.h>
4. #include <sys/un.h>
5. #include <errno.h>
6. #include <stddef.h>
7. #include <string.h>
8.
9. // the max connection number of the server
10. #define MAX_CONNECTION_NUMBER 5
11.
12. /* * Create a server endpoint of a connection. * Returns fd if all OK, <0 on error. */
13. int unix_socket_listen(const char *servername)
14. {
15. int fd;
16. struct sockaddr_un un;
17. if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
18. {
19. return(-1);
20. }
21. int len, rval;
22. /* in case it already exists */
23. sizeof(un));
24. un.sun_family = AF_UNIX;
25. strcpy(un.sun_path, servername);
26. struct sockaddr_un, sun_path) + strlen(servername);
27. /* bind the name to the descriptor */
28. if (bind(fd, (struct sockaddr *)&un, len) < 0)
29. {
30. rval = -2;
31. }
32. else
33. {
34. if (listen(fd, MAX_CONNECTION_NUMBER) < 0)
35. {
36. rval = -3;
37. }
38. else
39. {
40. return fd;
41. }
42. }
43. int err;
44. err = errno;
45. close(fd);
46. errno = err;
47. return rval;
48. }
49.
50. int unix_socket_accept(int listenfd, uid_t *uidptr)
51. {
52. int clifd, len, rval;
53. time_t staletime;
54. struct sockaddr_un un;
55. struct stat statbuf;
56. sizeof(un);
57. if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)
58. {
59. return(-1);
60. }
61. /* obtain the client's uid from its calling address */
62. struct sockaddr_un, sun_path); /* len of pathname */
63. /* null terminate */
64. if (stat(un.sun_path, &statbuf) < 0)
65. {
66. rval = -2;
67. }
68. else
69. {
70. if (S_ISSOCK(statbuf.st_mode) )
71. {
72. if (uidptr != NULL) *uidptr = statbuf.st_uid; /* return uid of caller */
73. /* we're done with pathname now */
74. return clifd;
75. }
76. else
77. {
78. /* not a socket */
79. }
80. }
81. int err;
82. err = errno;
83. close(clifd);
84. errno = err;
85. return(rval);
86. }
87.
88. void unix_socket_close(int fd)
89. {
90. close(fd);
91. }
92.
93. int main(void)
94. {
95. int listenfd,connfd;
96. "foo.sock");
97. if(listenfd<0)
98. {
99. "Error[%d] when listening...\n",errno);
100. return 0;
101. }
102. "Finished listening...\n",errno);
103. uid_t uid;
104. connfd = unix_socket_accept(listenfd, &uid);
105. unix_socket_close(listenfd);
106. if(connfd<0)
107. {
108. "Error[%d] when accepting...\n",errno);
109. return 0;
110. }
111. "Begin to recv/send...\n");
112. int i,n,size;
113. char rvbuf[2048];
114. for(i=0;i<2;i++)
115. {
116. //===========接收==============
117. size = recv(connfd, rvbuf, 804, 0);
118. if(size>=0)
119. {
120. // rvbuf[size]='\0';
121. "Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
122. }
123. if(size==-1)
124. {
125. "Error[%d] when recieving Data:%s.\n",errno,strerror(errno));
126. break;
127. }
128. /*
129. //===========发送==============
130. memset(rvbuf, 'c', 2048);
131. size = send(connfd, rvbuf, 2048, 0);
132. if(size>=0)
133. {
134. printf("Data[%d] Sended.\n",size);
135. }
136. if(size==-1)
137. {
138. printf("Error[%d] when Sending Data.\n",errno);
139. break;
140. }
141. */
142. sleep(30);
143. }
144. unix_socket_close(connfd);
145. "Server exited.\n");
146. }
客户端代码
[cpp] view plain copy
1. #include <stdio.h>
2. #include <stddef.h>
3. #include <sys/stat.h>
4. #include <sys/socket.h>
5. #include <sys/un.h>
6. #include <errno.h>
7. #include <string.h>
8.
9. /* Create a client endpoint and connect to a server. Returns fd if all OK, <0 on error. */
10. int unix_socket_conn(const char *servername)
11. {
12. int fd;
13. if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) /* create a UNIX domain stream socket */
14. {
15. return(-1);
16. }
17. int len, rval;
18. struct sockaddr_un un;
19. sizeof(un)); /* fill socket address structure with our address */
20. un.sun_family = AF_UNIX;
21. "scktmp%05d", getpid());
22. struct sockaddr_un, sun_path) + strlen(un.sun_path);
23. /* in case it already exists */
24. if (bind(fd, (struct sockaddr *)&un, len) < 0)
25. {
26. rval= -2;
27. }
28. else
29. {
30. /* fill socket address structure with server's address */
31. sizeof(un));
32. un.sun_family = AF_UNIX;
33. strcpy(un.sun_path, servername);
34. struct sockaddr_un, sun_path) + strlen(servername);
35. if (connect(fd, (struct sockaddr *)&un, len) < 0)
36. {
37. rval= -4;
38. }
39. else
40. {
41. return (fd);
42. }
43. }
44. int err;
45. err = errno;
46. close(fd);
47. errno = err;
48. return rval;
49. }
50.
51. void unix_socket_close(int fd)
52. {
53. close(fd);
54. }
55.
56.
57. int main(void)
58. {
59. int)time(0));
60. int connfd;
61. "foo.sock");
62. if(connfd<0)
63. {
64. "Error[%d] when connecting...",errno);
65. return 0;
66. }
67. "Begin to recv/send...\n");
68. int i,n,size;
69. char rvbuf[4096];
70. for(i=0;i<10;i++)
71. {
72. /*
73. //=========接收=====================
74. size = recv(connfd, rvbuf, 800, 0); //MSG_DONTWAIT
75. if(size>=0)
76. {
77. printf("Recieved Data[%d]:%c...%c\n",size,rvbuf[0],rvbuf[size-1]);
78. }
79. if(size==-1)
80. {
81. printf("Error[%d] when recieving Data.\n",errno);
82. break;
83. }
84. if(size < 800) break;
85. */
86. //=========发送======================
87. memset(rvbuf,'a',2048);
88. 'b';
89. size = send(connfd, rvbuf, 2048, 0);
90. if(size>=0)
91. {
92. "Data[%d] Sended:%c.\n",size,rvbuf[0]);
93. }
94. if(size==-1)
95. {
96. "Error[%d] when Sending Data:%s.\n",errno,strerror(errno));
97. break;
98. }
99. sleep(1);
100. }
101. unix_socket_close(connfd);
102. "Client exited.\n");
103. }
五、 讨论
通过实际测试,发现UNIXDomain Socket与命名管道在表现上有很大的相似性,例如,UNIX Domain Socket也会在磁盘上创建一个socket类型文件;如果读端进程关闭了,写端进程“写数据”时,有可能使进程异常退出,等等。查阅有关文档,摘录如下:
Send函数
当调用该函数时,send先比较待发送数据的长度len和套接字s的发送缓冲的 长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么 send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据copy到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据copy到s的发送缓冲区的剩余空间里)。如果send函数copy数据成功,就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
要注意send函数把buf中的数据成功copy到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
Recv函数与send类似,看样子系统在实现各种IPC时,有些地方是复用的。