一:概念: 
我们知道IP地址是标志网络中不用主机的IP地址,而端口号就是同一台主机上标志不同进程的地址,IP地址和端口号标志网络中的唯一地址.(又称socket) 
在TCP协议中,建⽴立连接的两个进程各⾃自有⼀一个socket来标识,那么这两个组成 的socket就唯⼀一标识⼀一个连接。socket本⾝身有“插座”的意思,因此⽤用来描述网络连接的一一 对应关系. 
二:通信中的大端小端的问题如何解决? 
我们知道每台机器的大小端都可能不同.网路中也一样,当两台主机进行通信的时候那么如何解决大小端的问题呢?小端:数据的低位在低地址,高位在高地址;大端:数据的低位在高地址,高位在低地址.网络数据流先发出的是低地址,后发出的是高地址.TCP/IP规定,网络数据流采用大端字节序,即就是低位在高地址.如果接受主机是小端序列,发送主机是大端序列那么如何解决它们之间的转化呢?

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换 
 
如果主机是小端字节序,这些函数将参数左相应的大小端转换后返回,如果是大端字节序,这些函数不做转换,将参数原封不动地返回. 
三:单进程的套接字通信 
服务器: 
1:调用socket,请求系统分配文件描述符 
 
参数类型: 
type的取值 
 
2:调用bing,绑定本机的信息,包括IP地址和端口号 
 
3:调用listen,监听 
 
函数功能:使上述socket返回的文件描述符sockfd进入监听状态;backlog:连接队列的长度,当一个请求次数超过长度时,就报错 
4:调用accept,接收连接请求的socket 
5:read读取socket中的数据 
6:通信完成后,调用close关闭套接字

客户端 
1:调用socket,请求系统分配文件描述符 
2:调用connect,连接服务器 
 
3:调用read函数标准输入中读取数据,放到自定义缓冲区buf中,然后将buf中的数据写到套接字中 
4;通信完成后,调用close关闭套接字. 
验证: 
Makafile文件

.PHONY:all
all:tcp_server tcp_client
tcp:tcp_server tcp_client
gcc- o $@ $^
tcp_client:tcp_client.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f tcp_server tcp_client
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

tcp_server文件:

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
int StartUp(int port,const char*ip)
{
int Listen =socket(AF_INET,SOCK_STREAM,0);
if(Listen<0)
{
perror("Listen");
exit(1);
}
struct sockaddr_in local;
local.sin_family =AF_INET;
local.sin_port =htons(port);
local.sin_addr.s_addr =inet_addr(ip);
if(bind(Listen,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("bind");
exit(2);
}
if(listen(Listen,5)<0)
{
perror("listen");
exit(3);
}
return Listen;
}
int main(int argc,const char*argv[])
{
if(argc !=3)
{
printf("input error\n");
return 1;
}
int len;
int Listen =StartUp(atoi(argv[2]),argv[1]);
struct sockaddr_in client;
while(1)
{
int sock = accept(Listen,(struct sockaddr*)&client,&len);
if(sock<0)
{
perror("accept");
continue;
}
printf("get a client ,ip is %s,part is%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
char buf[1024];
while(1)
{
ssize_t s =read(sock,buf,sizeof(buf)-1);//服务器读数据
if(s>0)
{
buf[s] =0;
printf("client #%s\n",buf);
}
else{
//数据读完了.客户端不发送数据
printf("client is quit!\n");
}
}
close(sock);
}
return 0;
}
  • 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

tcp_client文件:

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
int main(int argc,const char*argv[])
{
int sock =socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 1;
}
struct sockaddr_in server;
server.sin_family =AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr =inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0)
{
perror("connect");
return 2;
}
char buf[1024];
while(1)
{
printf("send#");
fflush(stdout);
//从标准输入读数据,读到buf中,然后从Buf写到管道中
ssize_t s =read(0,buf,sizeof(buf)-1);
if(s<0)
{
perror("read");
return 3;
}
buf[s-1] =0;
write(sock,buf,s);
}
close(sock);
return 0;
}
  • 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

结果: 
服务器 
 
 
客户端: 
 
一种现象:

 
这是什么原因呢?服务器终止程序,服务器就是主动发起断开连接请求的一方,根据TCP的3次握手4次挥手协议,主动发起连接断开请求的一方,最后必须等待2MSL的时间确认客户端是否收到自己的确认信息。这里,我们立即运行server的时候,server还是在TIME_WAIT状态,所以bind的时候就会出现地址已经被占用。 
解决办法:socket之后,bind之前,加语句 
int opt = 1; 
setsockopt(ListenSock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); 
就是为了处理这个问题。

四:多进程socket网络通信: 
在实际应用当中,可能有这种情况有多个客户端都给服务器发送数据,那么前面那种单进程的肯定有问题,这时我们需要多进程socket通信,就可以实现: 
实现方法:服务器端可以创建多个子进程去处理客户端发来的数据,当每次收到一个新的客户端的连接请求的时候,我们就会fork()出一个子进程,父进程用于等待子进程,子进程用于执行,读客户端法的数据的操作,终止子进程,儿子进程被它的父进程回收,此时的孙子进程就是一个孤儿进程,被1号进程(操作系统回收),这样做的目的就是,不要让儿子进程等待孙子进程太久而消耗太多的系统资源.

 
注意:父进程关闭套接字,子进程关闭监听套接字这时因为,父进程是来监听的,不需要通信子进程是读取信息的,不需要监听. 
验证:

Makefile文件

.PHONY:all 
all:server client 
server:server.c 
gcc -o @^ 
client:client.c 
gcc -o @^ 
.PHONY: 
rm -f server client

.server文件

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int Startup(int port,const char*ip)
{
int ListenSock =socket(AF_INET,SOCK_STREAM,0);
if(ListenSock<0)
{
perror("socket");
exit(1);
}
int opt =1;
setsockopt(ListenSock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family =AF_INET;
local.sin_port =htons(port);
local.sin_addr.s_addr =inet_addr(ip);
if(bind(ListenSock,(struct sockaddr*)&local,sizeof(local))<0)
{
perror("nind");
exit(2);
}
if(listen(ListenSock,5)<0)
{
perror("listen");
exit(3);
}
return ListenSock;
}
int main(int argc,const char*argv[])
{
if(argc!=3)
{
printf("input error\n");
return 1;
}
int len;
int ListenSocket =Startup(atoi(argv[2]),argv[1]);
struct sockaddr_in client;
while(1)
{
int sock =accept(ListenSocket,(struct sockaddr*)&client,&len);//获取客户端的信息
if(sock<0){
perror("accept");
continue;//获取失败继续accept
}
printf("get a client ,ip is %s,port is %d\n",inet_ntoa(client.sin_addr),\
ntohs(client.sin_port));
int id =fork();
if(id>0)
{
close(sock);
while(waitpid(-1,NULL,WNOHANG)>0);
continue;
}
else{
close(ListenSocket);
if(fork()>0)
{
exit(0);
}
char buf[1024];
while(1)
{
ssize_t s = read(sock,buf,sizeof(buf)-1);
if(s>0)
{
buf[s] =0;
printf("client # %s\n",buf);
}
else{
printf("client is quit\n");
break;
}
}
close(sock);
break;
}
}
return 0;
}
  • 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

.clent文件

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
int main(int argc,const char*argv[])
{
int sock =socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
return 1;
}
struct sockaddr_in server;
server.sin_family =AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&server,sizeof(server)) < 0)
{
perror("connect");
return 2; }
char buf[1024];
while(1)
{
printf("send# ");
fflush(stdout);
//从标准输入读数据,读到buf中,然后从buf写到管道
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s < 0)
{
perror("read");
break;
}
buf[s-1] = 0;
write(sock,buf,s); }
close(sock);
return 0;
}
  • 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

当然这个也可以跟上一种方法一样用本地环回测试但最好的方式还是连接多个主机都向向server主机发数据,观察现象. 
五:多线程的socket网络编程: 
再前面我们知道线程是进程内部的执行分支,是在进程的地址空间中运行,而进程的缺点时比较占用系统的资源,当用户过多时,经受不住太多用户的访问,所以我们的另外一种方法就是采用线程实现通信. 
方法
主线程创建出一个新线程,新线程的执行函数是读取信息,类似上边的多进程间的通信,我们可以将新的线程进行分离detch(),分离之后的线程就不需要主线程去等待,而是由操作系统区回收(这里最好不要用join线程等待,减小的等的时间) 
验证 
.Makefile文件: 
.PHONY:all 
all:server client 
server:server.c 
gcc -o @^ -lpthread 
client:client.c 
gcc -o @^ -lpthread 
.PHONY: 
rm -f server client 
.server文件

#include<stdio.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
int StartUp(int port,const char* ip)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
exit(2); }
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = inet_addr(ip);
if(bind(sock,(struct sockaddr*)&local,sizeof(local)) < 0)
{
perror("bind");
exit(3); }
if(listen(sock,5) < 0)
{
perror("listen");
exit(4); }
return sock;
}
void* thread_hander(void* arg)
{
int sock = *((int*)arg);
char buf[1024];
while(1)
{
ssize_t _s = read(sock,buf,sizeof(buf)-1);
if(_s > 0)
{
buf[_s-1] = 0;
printf("client say#%s\n",buf);
if(write(sock,buf,sizeof(buf)-1)<0)
{
break; } }
else if(_s == 0)
{
printf("client is quit!\n");
break; }
else
{
perror("read");
break; } }
close(sock);
}
int main(int argc,const char* argv[])
{
if(argc != 3)
{
printf("input error\n");
return 1; }
int listenSock = StartUp(atoi(argv[2]),argv[1]);
struct sockaddr_in client;
int len = 0;
while(1)
{
int sock = accept(listenSock,(struct sockaddr*)&client,&len);
if(sock < 0)
{
perror("accept");
return 5; }
printf("get a client!ip is %s,port is %d\n",inet_ntoa(client.sin_addr),\
ntohs(client.sin_port));
pthread_t tid;
int ret = pthread_create(&tid,NULL,thread_hander,&sock);
if(ret < 0)
{
perror("pthread_create");
return 6; }
pthread_detach(tid); }
return 0;
}
  • 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
  • 101
  • 102
  • 103
  • 104

.client文件

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
int main(int argc, const char* argv[])
{
if(argc != 3)
{
printf("input error\n");
return 1; }
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0)
{
perror("socket");
return 2; }
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = inet_addr(argv[1]);
int ret = connect(sock,(struct sockaddr*)&server,sizeof(server));
if(connect < 0)
{
perror("connect");
return 3; }
char buf[1024];
while(1)
{
printf("send#");
fflush(stdout);
ssize_t _s = read(0,buf,sizeof(buf)-1);
if(_s > 0)
{
buf[_s - 1] = 0;
if(write(sock,buf,sizeof(buf)-1) < 0)
{
break; }
ssize_t s = read(sock,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s] = 0;
printf("server echo#%s\n",buf); } }
else
{
perror("read");
return 4; } }
return 0;
}

socket--多进程,多线程服务器的更多相关文章

  1. c++下基于windows socket的多线程服务器(基于TCP协议)

    之前用c++实现过基于windows socket的单线程TCP服务器(http://www.cnblogs.com/jzincnblogs/p/5170230.html),今天实现了一个多线程的版本 ...

  2. php socket多进程简单服务器(一)

    进程,线程  IO复用,协程都是处理完成并发的方式 socket分为  三步 服务器监听,客户端请求,连接确认, 每次连接都由当前进程来处理,可以通过IO复用来解决这个问题, 这次通过进程来完成并发请 ...

  3. python多进程、多线程服务器和客户端的简单实现

    使用了多进程的服务器: from SocketServer import TCPServer, ForkingMixIn, ThreadingMixIn, StreamRequestHandler c ...

  4. 基于多进程和基于多线程服务器的优缺点及nginx服务器的启动过程

    基于多进程服务器的优点: 1.由操作系统进行调度,运行比较稳定强壮 2.能够方便地通过操作系统进行监控和管理 例如对每个进程的内存变化状况,甚至某个进程处理什么web请求进行监控.同时可以通过给进程发 ...

  5. 【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

    RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [ ...

  6. Python socket进阶 多线程/进程

    #首先,什么场合下用进程,什么场合下用线程: . 计算密集型的用进程. . IO密集型的用进程. xSocket语法及相关 Socket Families(地址簇) socket.AF_UNIX un ...

  7. Python 多进程 多线程 协程 I/O多路复用

    引言 在学习Python多进程.多线程之前,先脑补一下如下场景: 说有这么一道题:小红烧水需要10分钟,拖地需要5分钟,洗菜需要5分钟,如果一样一样去干,就是简单的加法,全部做完,需要20分钟:但是, ...

  8. python day 20: 线程池与协程,多进程TCP服务器

    目录 python day 20: 线程池与协程 2. 线程 3. 进程 4. 协程:gevent模块,又叫微线程 5. 扩展 6. 自定义线程池 7. 实现多进程TCP服务器 8. 实现多线程TCP ...

  9. 基于事件的 NIO 多线程服务器--转载

    JDK1.4 的 NIO 有效解决了原有流式 IO 存在的线程开销的问题,在 NIO 中使用多线程,主要目的已不是为了应对每个客户端请求而分配独立的服务线程,而是通过多线程充分使用用多个 CPU 的处 ...

  10. python socket之tcp服务器与客户端demo

    python socket之tcp服务器与客户端demo 作者:vpoet mails:vpoet_sir@163.com server: # -*- coding: cp936 -*- ''' 建立 ...

随机推荐

  1. 【FCS NOI2018】福建省冬摸鱼笔记 day2

    第二天. 同学还是不带本子记笔记.dalao. 第二天:图论,讲师:@ExfJoe 全程划水,前面都讲水算法[虽然我可能已经忘记了]什么最短路,Tarjan,最小生成树,2SAT,差分约束啥的,我现在 ...

  2. 【codeforces】【比赛题解】#851 CF Round #432 (Div.2)

    cf真的难…… 点我浏览丧题. [A]Arpa和她对墨西哥人浪的研究 Arpa正在对墨西哥人浪进行研究. 有n个人站成一排,从1到n编号,他们从时刻0开始墨西哥人浪. 在时刻1,第一个人站起来.在时刻 ...

  3. linux下补丁制作及打补丁实例【转】

    转自:http://www.latelee.org/using-gnu-linux/diff-and-patch-on-linux.html 搞ARM有一段时日了,期间看了不少开发板的手册,手册的内容 ...

  4. shell脚本编程之“最简单的死循环”【转】

    转自:http://blog.chinaunix.net/uid-23046336-id-3475462.html 在linux下编程的程序猿都知道shell脚本,就算你不怎么熟悉,也应该听过的吧!那 ...

  5. 大数据系列之分布式计算批处理引擎MapReduce实践

    关于MR的工作原理不做过多叙述,本文将对MapReduce的实例WordCount(单词计数程序)做实践,从而理解MapReduce的工作机制. WordCount: 1.应用场景,在大量文件中存储了 ...

  6. Eclipse中各种编码格式及设置

    操作系统:Windows 10(家庭中文版) Eclipse版本:Version: Oxygen.1a Release (4.7.1a) 刚看到一篇文章,里面介绍说Ascii.Unicode是编码,而 ...

  7. Vue项目之IE下打开页面是空白

    原因是:Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API ,比如 Iterator.Generator.Set.Maps.Proxy.Reflect.Sym ...

  8. HDU 3861 The King’s Problem(强连通分量+最小路径覆盖)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3861 题目大意: 在csdn王国里面, 国王有一个新的问题. 这里有N个城市M条单行路,为了让他的王国 ...

  9. Robust Mesh Watermarking

    之前看了一篇题为"Robust Mesh Watermarking"的论文,查阅资料的时候发现了一篇与之很相似的名为"三维模型数字水印系统的设计与实现"的中文论 ...

  10. 网络编程--Socket与ServerSocket

    1.服务器端代码 package net; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Sock ...