微信h5棋牌架設(aqiulian.com)多線程,多程序伺服器的實作,完整的微信h5棋牌架設,搭建教程聯系方式: QQ:212303635.
socket
在TCP/IP協定中,“IP位址+TCP/UDP端口号”表示唯一網絡通信中的一個程序,IP位址+端口号稱為socket。
注意:
位元組序有大端和小端,在兩台使用不同位元組序的主機上通信,為了讓兩台主機間能正确的通信,發送端總是把位元組序轉成大端位元組序資料後在發送,接收方知道接受的一定是大端位元組序,然後根據自己的位元組序進行轉化就不會出錯。
調用函數
int socket(int domain, int type, int protocol)
- 第一個參數是告訴系統使用哪個底層協定族。對于TCP/IP協定族該參數應該設定為AF_INET/PF_INET用于IPV4,兩個值相同可以混用。
- 第二個參數 SOCK_STREAM表示傳輸層使用TCP,SOCK_DGRAM表式UDP。
- 第三個參數通常都設為0,使用預設協定。
bind是将my_addr所指的socket配置設定到未命名的sockfd檔案描述符。int bind(int sockfd, struct sockaddr* my_addr, socklen_t len) struct sockaddr_in { sa_family_t sin_family; //位址族; AF_INET u_int16_t sin_port; //端口号 struct in_addr sin_addr; //IPV4位址結構體 } struct in_addr { u_int32_t s_addr; //IPV4位址 }
int listen(int sockfd, int backlog)
socket被命名之後不能馬上連接配接,需要listen建立一個監聽隊列,sockfd是指被監聽的socket,backlog是指處于連接配接狀态和socket的上限。
int accept(int sockfd, struct sockaddr*addr, socklen_t *len)
從listen監聽隊列中接受一個連接配接。sockfd是執行過linten接受監聽的socket。
單程序的server
/*************************************************************************
> File Name: tcp_server.c
> Author: weierxiao
> Mail: [email protected]
> Created Time: Sat 08 Jul 2017 05:15:29 PM CST
************************************************************************/
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock = socket(AF_INET, SOCK_STREAM,0);
if (sock < 0)
{
printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
}
struct sockaddr_in server_sock;
struct sockaddr_in client_sock;
bzero(&server_sock, sizeof(server_sock));
server_sock.sin_family = AF_INET;
server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
server_sock.sin_port = htons(_PORT_);
if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
{
printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 1;
}
if (listen(sock, _BACKLOG_)< 0)
{
printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 2;
}
printf("bind and listen success, wait accept!\n");
while (1)
{
socklen_t len = 0;
int client_socket = accept(sock, (struct sockaddr*)&client_sock,&len);
if (client_socket < 0)
{
printf("accept error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip, 0, sizeof(buf_ip));
inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));
printf("get connection :ip is %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );
while (1)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
read(client_socket, buf, sizeof(buf));
printf("client# : %s\n", buf);
printf("server# :");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1 ]= \'\0\';
write(client_socket, buf, strlen(buf)+1);
printf("please wait ...\n");
}
}
close(sock);
return 0;
}
client用戶端
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<arpa/inet.h>
#define SERVER_PORT 9999
#define SERVER_IP "192.168.2.103"
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage : client IP\n");
return 1;
}
char *ip = argv[1];
char buf[1024];
memset(buf,0,sizeof(buf));
struct sockaddr_in server_sock;
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
}
bzero(&server_sock, sizeof(server_sock));
server_sock.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &server_sock.sin_addr);
server_sock.sin_port = htons(SERVER_PORT);
int ret = connect(sock, (struct sockaddr*)& server_sock, sizeof(server_sock));
if (ret < 0)
{
printf("connect error, errno is %d, errstring is %s\n", errno, strerror(errno));
return 1;
}
printf("connect sucess!\n");
while(1)
{
printf("client# :");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = \'\0\';
write(sock, buf, sizeof(buf));
if (strncasecmp(buf,"quit", 4) == 0)
{
printf("quit !\n");
break;
}
printf("please wait\n");
read(sock, buf, sizeof(buf));
printf("server #:%s\n", buf);
}
close(sock);
return 0;
}
我主機的ip位址是192.168.2.103.
運作server
用指令netstat _nltp 可以檢視目前主機的tcp服務
我綁定的是9999端口,ip位址還沒确定,運作用戶端進行通信
多程序伺服器
将通信部分讓子程序去處理,其他過程讓父程序來處理。但是問題來了,隻要伺服器一直運作,我們的父程序就不會結束,而子程序随時可能結束,這樣一來子程序就變成了僵屍程序,那麼這個問題怎麼處理呢?其實處理這個問題非常巧妙,我們讓子程序再fork一次,得到一個孫子程序,然後結束子程序,這樣孫子程序就成了孤兒程序,它會被1号程序回收,在這個過程中并沒有産生僵屍程序,這個問題就解決了。
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock = socket(AF_INET, SOCK_STREAM,0);
if (sock < 0)
{
printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
}
struct sockaddr_in server_sock;
struct sockaddr_in client_sock;
bzero(&server_sock, sizeof(server_sock));
server_sock.sin_family = AF_INET;
server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
server_sock.sin_port = htons(_PORT_);
if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
{
printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 1;
}
if (listen(sock, _BACKLOG_)< 0)
{
printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 2;
}
printf("bind and listen success, wait accept!\n");
while (1)
{
socklen_t len = 0;
int client_socket = accept(sock, (struct sockaddr*)&client_sock,&len);
if (client_socket < 0)
{
printf("accept error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 3;
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip, 0, sizeof(buf_ip));
inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));
printf("get connection :ip is %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );
pid_t id = fork();
if (id<0)
{
perror("fork");
}
else if(id == 0)
{
close(sock);
pid_t idd = fork();
if (idd < 0)
{
perror("second fork");
exit(5);
}
else if (idd == 0)
{
while (1)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
read(client_socket, buf, sizeof(buf));
printf("client# : %s\n", buf);
printf("server# :");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1 ]= \'\0\';
write(client_socket, buf, strlen(buf)+1);
printf("please wait ...\n");
}
}
else
{
exit(6);
}
}
else
{
close(client_socket);
waitpid(id, NULL,0);
}
}
close(sock);
return 0;
}
多線程伺服器
多線程伺服器就是将通信部分讓一個線程去處理,為了避免線程退出時整個程序退出,我們将線程分離出去。
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>
#include<string.h>
#define _PORT_ 9999
#define _BACKLOG_ 10
void request(void* arg)
{
int new_fd = (int )arg;
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip, 0, sizeof(buf_ip));
inet_ntop(AF_INET, &client_sock.sin_addr, buf_ip, sizeof(buf_ip));
printf("get connection :ip is %s, port is %d\n ",buf_ip, ntohs(client_sock.sin_port) );
while (1)
{
char buf[1024];
memset(buf, 0, sizeof(buf));
read(client_socket, buf, sizeof(buf));
printf("client# : %s\n", buf);
printf("server# :");
memset(buf, 0, sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf) - 1 ]= \'\0\';
write(client_socket, buf, strlen(buf)+1);
printf("please wait ...\n");
}
}
}
int main()
{
int sock = socket(AF_INET, SOCK_STREAM,0);
if (sock < 0)
{
printf("socket error, errno is %d, errstring is %s\n", errno, strerror(errno));
}
struct sockaddr_in server_sock;
struct sockaddr_in client_sock;
bzero(&server_sock, sizeof(server_sock));
server_sock.sin_family = AF_INET;
server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
server_sock.sin_port = htons(_PORT_);
if (bind(sock, (struct sockaddr*)&server_sock, sizeof(struct sockaddr_in))< 0)
{
printf("bind error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 1;
}
if (listen(sock, _BACKLOG_)< 0)
{
printf("listen error, errno is %d, errstring is %s\n", errno, strerror(errno));
close(sock);
return 2;
}
printf("bind and listen success, wait accept!\n");
pthread_t id;
pthread_create(&id, NULL, request, (void*)new_fd );
pthread_detach(id);
close(sock);
return 0;
}
bind失敗的原因
當兩個 socket 的 address 和 port 相沖突,而你又想重用位址和端口,則舊的 socket 和新的 socket 都要已經被設定了 SO_REUSEADDR 特性,隻 有兩者之一有這個特性還是有問題的。
SO_REUSEADDR 可以用在以下四種情況下。
( 摘自《 Unix 網絡程式設計》卷一,即 UNPv1)
1 、當有一個有相同本地位址和端口的 socket1 處于 TIME_WAIT 狀态時,而你啟
動的程式的 socket2 要占用該位址和端口,你的程式就要用到該選項。
2 、 SO_REUSEADDR 允許同一 port 上啟動同一伺服器的多個執行個體 ( 多個程序 ) 。但
每個執行個體綁定的 IP 位址是不能相同的。在有多塊網卡或用 IP Alias 技術的機器可
以測試這種情況。
3 、 SO_REUSEADDR 允許單個程序綁定相同的端口到多個 socket 上,但每個 soc
ket 綁定的 ip 位址不同。這和 2 很相似,差別請看 UNPv1 。
4 、 SO_REUSEADDR 允許完全相同的位址和端口的重複綁定。但這隻用于 UDP 的
多點傳播,不用于 TCP 。
