1 /**********************hsj**************************/
2 #include<stdio.h>
3 #include<stdlib.h>
4 #include<string.h>
5 #include<unistd.h>
6 #include<sys/types.h>
7 #include<sys/socket.h>
8 #include<arpa/inet.h>
9 #include<pthread.h>
10 #define IP "192.168.2.140"
11 #define PORT 8000
12
✹ 13 int main(int argc, char* argv[]){
14 int pid;
15 char buf[1024];
16 int lfd = socket(AF_INET , SOCK_STREAM , 0);
17 if(lfd < 0){
18 perror("socket error");
19 exit(1);
20 }
21 struct sockaddr_in seraddr, cliaddr;
22 seraddr.sin_family = AF_INET;
23 seraddr.sin_port = htons(PORT);
24 // int dst;
25 // inet_pton(AF_INET,IP,(void*)&dst);
26 // seraddr.sin_addr.s_addr = dst;
27 seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
28 int optval = 1;
29 setsockopt(lfd , SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
30 int rr = bind(lfd , (struct sockaddr*)&seraddr, sizeof(seraddr));
31 if(rr < 0){
32 perror("bind error");
33 exit(1);
34 }
35 listen(lfd , 64);
36 socklen_t addrlen = sizeof(cliaddr);
37 while(1){
38 int cfd = accept(lfd, (struct sockaddr*)&cliaddr,&addrlen);
39 char arr[64];
40 inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, arr, 64);
41 printf("client is ok IP: %s, port: %d\n",arr,ntohs(cliaddr.sin_port));
42 pid = fork();
43 if(pid == 0){
44 close(lfd);
45 while(1){
46 rr = read(cfd, buf ,1024);
47 if(rr == 0){
48 printf("客户端断开连接\n");
49 close(cfd);
50 return 0;
51 }
52 else if(rr < 0){
53 perror("read error");
54 exit(1);
55 }
56 write(STDOUT_FILENO, buf, rr);
57 write(cfd, buf, rr);
58 }
59
60 }
61 close(cfd);
62
63 }
64 return 0;
65 }
66 /***************************************************/
这是多进程的服务器代码.
解决IP地址变化的问题:【之前IP地址写在代码里,IP动态变化,需要经常改代码】
将inetpton改为一个宏
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);//该宏为0.0.0.0(即是0)
让其取监听本地的IP地址,无论你的地址变成什么都没有问题
之前是宏定义IP,现在是直接给IP赋值,而inet_pton需要IP,htonl也可以得到网络字节序
解决nc只能连接一个客户端的问题(只有一个 accept):
设置一个属性:端口复用 setsockopt
获取属性getsockoppt
int setsockopt(int sockfd, socklen_t *)
sockfd:文件描述符
level:一个宏
允许地址复用:SO_REUSEADDR
允许端口复用:SO_REUSEPORT
optname:宏
optval:opt是选项,val是值,是一个数(存缓冲区)
0是不允许,1是允许
optlen:选项的长度(缓冲区的长度)
/**********************hsj**************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<error.h>
#define IP "192.168.2.140"
#define PORT 8000
void* client(void* arg)
{
int rr;
int cfd = *((int*)arg);
char buf[1024];
while(1)
{
rr = read(cfd, buf, 1024);
if(rr == 0)
{
printf("客户端断开连接\n");
close(cfd);
pthread_exit(NULL);
}
else if(rr < 0)
{
perror("read error");
pthread_exit(NULL);
}
write(STDOUT_FILENO, buf, rr);
write(cfd, buf, rr);
}
}
int main(int argc, char* argv[]){
pthread_t tid;
int lfd = socket(AF_INET , SOCK_STREAM , 0);
if(lfd < 0){
perror("socket error");
exit(1);
}
struct sockaddr_in seraddr, cliaddr;
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(PORT);
// int dst;
// inet_pton(AF_INET,IP,(void*)&dst);
// seraddr.sin_addr.s_addr = dst;
seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
int optval = 1;
setsockopt(lfd , SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
int rr = bind(lfd , (struct sockaddr*)&seraddr, sizeof(seraddr));
if(rr < 0){
perror("bind error");
exit(1);
}
listen(lfd , 64);
socklen_t addrlen = sizeof(cliaddr);
while(1){
int cfd = accept(lfd, (struct sockaddr*)&cliaddr,&addrlen);
char arr[64];
inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, arr, 64);
printf("client is ok IP: %s, port: %d\n",arr,ntohs(cliaddr.sin_port));
int ret = pthread_create(&tid, NULL,client,(void*)&cfd);
if(ret > 0){
printf("pthread_create error:%s\n", strerror(ret));
pthread_exit(NULL);
}
}
return 0;
}
/***************************************************/
这是服务器多线程的代码
思考一个问题:
client读取标准输入,写到buf缓冲区中,再讲buf缓冲区中的东西写入到cfd文件描述符中(描述的是socket管道)。
然后阻塞,等待服务器端写入,再读取写到标准输出。可以看到,读取的是cfd,但是之前客户端却写入了cfd,若是服务器除了什么问题,那么有没有可能客户端是在自己读自己。(因为这是回发服务器,对其来说写和回是相同的)
这与之前的C—S间发送信息的接收端和发送端有关。首先,客户端在自己的read上阻塞等待输入,当我们输入数据时,客户端将输入的数据写在buf里,再将buf里的数据写在cfd套接字的发送端中,此时我们的服务器从cfd的发送端缓冲区读出数据,再把读出的数据打在标准输出中,再写入cfd套接字的接收端缓冲区,此时到了客户端读到了数据写在buf里,再从buf里打印数据形成回射。